/// <summary>
        /// Handle output from API
        /// </summary>
        /// <param name="context"></param>
        private async Task <bool> PerformOutput(HttpContext context)
        {
            //decide correct reponse type
            IOutputProvider outputProvider = null;
            var             filter         = new APIRequestParams();

            //set defaults
            string outputType = "xml";

            filter.DistanceUnit  = DistanceUnit.Miles;
            filter.MaxResults    = 100;
            filter.EnableCaching = true;

            var paramList = new NullSafeDictionary <string, string>();

            foreach (var k in context.Request.Query.Keys)
            {
                paramList.Add(k.ToLower(), context.Request.Query[k]);
            }
            filter.ParseParameters(filter, paramList);

            if (string.IsNullOrEmpty(filter.APIKey))
            {
                if (context.Request.Headers.ContainsKey("X-API-Key"))
                {
                    filter.APIKey = context.Request.Headers["X-API-Key"];
                }
            }

            //override ?v=2 etc if called via /api/v2/ or /api/v1
            if (APIBehaviourVersion > 0)
            {
                filter.APIVersion = APIBehaviourVersion;
            }
            if (APIBehaviourVersion >= 2)
            {
                filter.Action = DefaultAction;
            }

            try
            {
                if ((_settings.IsCacheOnlyMode || context.Request.GetUri().Host.ToLower().StartsWith("api")) && (filter.APIVersion == null || filter.APIVersion == 1))
                {
                    //API version is mandatory for api V2 onwards via api.openchargemap.* hostname or for API mirrors operating in cached mode
                    await OutputBadRequestMessage(context, "mandatory API Version not specified in request");

                    return(true);
                }
            }
            catch (System.UriFormatException)
            {
                _logger?.LogWarning("Failed to parse URI " + context.Request.Host.Value);
            }

            if (!String.IsNullOrEmpty(context.Request.Query["output"]))
            {
                outputType = ParseString(context.Request.Query["output"]).ToLower();
            }
            else
            {
                //new default after API V2 is json instead of XML
                if (filter.APIVersion >= 2)
                {
                    outputType = "json";
                }
            }

            //change defaults and override settings for deprecated api features
            if (filter.APIVersion >= 2)
            {
                //the following output types are deprecated and will default as JSON
                if (outputType == "carwings" || outputType == "rss")
                {
                    await OutputBadRequestMessage(context, "specified output type not supported in this API version");

                    return(true);
                }
            }

            if (_settings.IsCacheOnlyMode)
            {
                filter.AllowMirrorDB    = true;
                filter.AllowDataStoreDB = false;
            }

            if (IsRequestByRobot(context))
            {
                await OutputBadRequestMessage(context, "API requests by robots are temporarily disabled.", statusCode : 503);

                return(true);
            }

            //determine output provider
            switch (outputType)
            {
            case "xml":
                outputProvider = new XMLOutputProvider();
                break;

            case "carwings":
            case "rss":
                outputProvider = new RSSOutputProvider();
                if (outputType == "carwings")
                {
                    ((RSSOutputProvider)outputProvider).EnableCarwingsMode = true;
                }
                break;

            case "json":
                outputProvider = new JSONOutputProvider();
                break;

            case "geojson":
                outputProvider = new GeoJSONOutputProvider();
                break;

            case "csv":
                outputProvider = new CSVOutputProvider();
                break;

            case "kml":
                outputProvider = new KMLOutputProvider(KMLOutputProvider.KMLVersion.V2);
                break;

            default:
                outputProvider = new XMLOutputProvider();
                break;
            }

            if (outputProvider != null)
            {
                context.Response.ContentType = outputProvider.ContentType;

                if (filter.Action == "getchargepoints" || filter.Action == "poi")
                {
                    await OutputPOIList(outputProvider, context, filter);

                    return(true);
                }

                if (filter.Action == "getcompactpoilist")
                {
                    //experimental::
                    await OutputCompactPOIList(context, filter);

                    return(true);
                }

                if (filter.Action == "getcorereferencedata")
                {
                    await OutputCoreReferenceData(outputProvider, context, filter);

                    return(true);
                }

                if (filter.Action == "geocode")
                {
                    OutputGeocodingResult(outputProvider, context, filter);
                    return(true);
                }

                if (filter.Action == "availability")
                {
                    OutputAvailabilityResult(outputProvider, context, filter);
                    return(true);
                }

                if (filter.Action == "profile.authenticate")
                {
                    await OutputProfileSignInResult(outputProvider, context, filter);

                    return(true);
                }

                if (filter.Action == "profile.register")
                {
                    await OutputProfileRegisterResult(outputProvider, context, filter);

                    return(true);
                }

                if (filter.Action == "refreshmirror")
                {
                    try
                    {
                        var itemsUpdated = await OCM.Core.Data.CacheManager.RefreshCachedData();

                        await new JSONOutputProvider().GetOutput(context, context.Response.Body, new { POICount = itemsUpdated, Status = "OK" }, filter);
                        return(true);
                    }
                    catch (Exception exp)
                    {
                        await new JSONOutputProvider().GetOutput(context, context.Response.Body, new { Status = "Error", Message = exp.ToString() }, filter);
                        return(true);
                    }
                }
            }

            // request did not match an existing handler
            return(false);
        }