Example #1
0
        /// <inheritdoc />
        /// <remarks>
        /// <para>This method do not check the HTTP status code, because for certain JSON responses,
        /// the status code might has its semantic meanings.</para>
        /// <para>Then the content will be parsed as JSON, in <see cref="JToken"/>. If there is
        /// <see cref="JsonException"/> thrown while parsing the response, a retry will be requested.</para>
        /// <para>The default implementation for this method throws a <see cref="WikiaApiException"/>
        /// or one of its derived exceptions when detected <c>exception</c> node in the JSON response.
        /// The exception mapping is as follows</para>
        /// <list type="table">
        /// <listheader>
        /// <term><c>exception.code</c> value</term>
        /// <description>Mapped exception type</description>
        /// </listheader>
        /// <item>
        /// <term><c>NotFoundApiException</c></term>
        /// <description><see cref="NotFoundApiException"/></description>
        /// </item>
        /// <item>
        /// <term>Others</term>
        /// <description><see cref="WikiaApiException"/></description>
        /// </item>
        /// </list>
        /// </remarks>
        public override async Task <JToken> ParseResponseAsync(HttpResponseMessage response, WikiResponseParsingContext context)
        {
            if (response == null)
            {
                throw new ArgumentNullException(nameof(response));
            }
            if (context == null)
            {
                throw new ArgumentNullException(nameof(context));
            }
            // For REST-ful API, we need to parse the content first, to see what happened.
            JToken jroot;

            try
            {
                jroot = await MediaWikiHelper.ParseJsonAsync(await response.Content.ReadAsStreamAsync(),
                                                             context.CancellationToken);
            }
            catch (JsonException)
            {
                context.NeedRetry = true;
                throw;
            }
            if (jroot is JObject obj)
            {
                // Check for exception node.
                var exception = obj["exception"];
                if (exception != null)
                {
                    var type    = (string)exception["type"];
                    var message = (string)exception["message"];
                    var details = (string)exception["details"];
                    var code    = (int?)exception["code"] ?? (int)response.StatusCode;
                    var traceId = (string)jroot["trace_id"];
                    switch (type)
                    {
                    case "NotFoundApiException":
                        throw new NotFoundApiException(type, message, code, details, traceId);

                    default:
                        throw new WikiaApiException(type, message, code, details, traceId);
                    }
                }
            }
            return(jroot);
        }
        /// <inheritdoc />
        /// <remarks>
        /// <para>This method checks the HTTP status code first.
        /// For non-successful HTTP status codes, this method will request for a retry.</para>
        /// <para>Then the content will be parsed as JSON, in <see cref="JToken"/>. If there is
        /// <see cref="JsonException"/> thrown while parsing the response, a retry will be requested.</para>
        /// <para>Finally, before returning the parsed JSON, this method checks for <c>warning</c> and <c>error</c>
        /// nodes. If there exists <c>warning</c> node, a warning will be issued to the logger. If there exists <c>error</c>
        /// node, a <see cref="OperationFailedException"/> or its derived exception will be thrown. You can
        /// customize the error generation behavior by overriding <see cref="OnApiError"/> method.</para>
        /// </remarks>
        public override async Task <JToken> ParseResponseAsync(HttpResponseMessage response, WikiResponseParsingContext context)
        {
            if (response == null)
            {
                throw new ArgumentNullException(nameof(response));
            }
            if (context == null)
            {
                throw new ArgumentNullException(nameof(context));
            }
            // Check response status first.
            if (!response.IsSuccessStatusCode)
            {
                context.NeedRetry = true;
                response.EnsureSuccessStatusCode();
            }
            JToken jroot;

            try
            {
                await using var s = await response.Content.ReadAsStreamAsync();

                jroot = await MediaWikiHelper.ParseJsonAsync(s, context.CancellationToken);
            }
            catch (JsonException)
            {
                // Input is not valid json.
                context.NeedRetry = true;
                throw;
            }
            // See https://www.mediawiki.org/wiki/API:Errors_and_warnings .

            /*
             * "warnings": {
             * "main": {
             * "*": "xxxx"
             * },
             * "login": {
             * "*": "xxxx"
             * }
             * }
             */
            // Note that in MW 1.19, action=logout returns [] instead of {}
            if (jroot is JObject jobj)
            {
                if (jobj["warnings"] != null && context.Logger.IsEnabled(LogLevel.Warning))
                {
                    foreach (var module in ((JObject)jobj["warnings"]).Properties())
                    {
                        context.Logger.LogWarning("API warning [{Module}]: {Warning}", module.Name, module.Value);
                    }
                }
                var err = jobj["error"];
                if (err != null)
                {
                    var errcode = (string)err["code"];
                    // err["*"]: API usage.
                    var errmessage = ((string)err["info"]).Trim();
                    context.Logger.LogWarning("API error: {Code} - {Message}", errcode, errmessage);
                    OnApiError(errcode, errmessage, err, jobj, context);
                }
            }
            return(jroot);
        }