/// <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); }