diff --git a/src/Snap.Hutao/Snap.Hutao/Model/Calculable/CalculableOptions.cs b/src/Snap.Hutao/Snap.Hutao/Model/Calculable/CalculableOptions.cs index 4a8e54b9..b77c6898 100644 --- a/src/Snap.Hutao/Snap.Hutao/Model/Calculable/CalculableOptions.cs +++ b/src/Snap.Hutao/Snap.Hutao/Model/Calculable/CalculableOptions.cs @@ -3,26 +3,12 @@ namespace Snap.Hutao.Model.Calculable; -/// -/// 可计算物品选项 -/// internal readonly struct CalculableOptions { - /// - /// 角色 - /// public readonly ICalculableAvatar? Avatar; - /// - /// 武器 - /// public readonly ICalculableWeapon? Weapon; - /// - /// 构造一个新的可计算物品选项 - /// - /// 角色 - /// 武器 public CalculableOptions(ICalculableAvatar? avatar, ICalculableWeapon? weapon) { Avatar = avatar; diff --git a/src/Snap.Hutao/Snap.Hutao/Web/Hoyolab/Takumi/GameRecord/GameRecordClient.cs b/src/Snap.Hutao/Snap.Hutao/Web/Hoyolab/Takumi/GameRecord/GameRecordClient.cs index 8659d6e1..f018970d 100644 --- a/src/Snap.Hutao/Snap.Hutao/Web/Hoyolab/Takumi/GameRecord/GameRecordClient.cs +++ b/src/Snap.Hutao/Snap.Hutao/Web/Hoyolab/Takumi/GameRecord/GameRecordClient.cs @@ -228,7 +228,7 @@ internal sealed partial class GameRecordClient : IGameRecordClient .ConfigureAwait(false); // We have a verification procedure to handle - if (resp?.ReturnCode == (int)KnownReturnCode.CODE1034) + if (resp?.ReturnCode is (int)KnownReturnCode.CODE1034) { // Replace message resp.Message = SH.WebIndexOrSpiralAbyssVerificationFailed; diff --git a/src/Snap.Hutao/Snap.Hutao/Web/Request/Builder/HttpRequestMessageBuilder.cs b/src/Snap.Hutao/Snap.Hutao/Web/Request/Builder/HttpRequestMessageBuilder.cs index fad7ec07..ebf5f004 100644 --- a/src/Snap.Hutao/Snap.Hutao/Web/Request/Builder/HttpRequestMessageBuilder.cs +++ b/src/Snap.Hutao/Snap.Hutao/Web/Request/Builder/HttpRequestMessageBuilder.cs @@ -8,7 +8,7 @@ using System.Net.Http.Headers; namespace Snap.Hutao.Web.Request.Builder; -internal class HttpRequestMessageBuilder : +internal sealed class HttpRequestMessageBuilder : IBuilder, IHttpRequestMessageBuilder, IHttpHeadersBuilder, @@ -22,12 +22,14 @@ internal class HttpRequestMessageBuilder : IHttpMethodBuilder { private readonly HttpContentSerializer httpContentSerializer; + private readonly IServiceProvider serviceProvider; private HttpRequestMessage httpRequestMessage; - public HttpRequestMessageBuilder(HttpContentSerializer httpContentSerializer, HttpRequestMessage? httpRequestMessage = default) + public HttpRequestMessageBuilder(IServiceProvider serviceProvider, HttpContentSerializer httpContentSerializer, HttpRequestMessage? httpRequestMessage = default) { + this.serviceProvider = serviceProvider; this.httpContentSerializer = httpContentSerializer; - this.httpRequestMessage = httpRequestMessage ?? new HttpRequestMessage(); + this.httpRequestMessage = httpRequestMessage ?? new(); } public HttpRequestMessage HttpRequestMessage @@ -40,6 +42,8 @@ internal class HttpRequestMessageBuilder : } } + public IServiceProvider ServiceProvider { get => serviceProvider; } + public HttpContentSerializer HttpContentSerializer { get => httpContentSerializer; } HttpContentHeaders IHttpHeadersBuilder.Headers diff --git a/src/Snap.Hutao/Snap.Hutao/Web/Request/Builder/HttpRequestMessageBuilderExtension.cs b/src/Snap.Hutao/Snap.Hutao/Web/Request/Builder/HttpRequestMessageBuilderExtension.cs index b834f07e..e5d643f6 100644 --- a/src/Snap.Hutao/Snap.Hutao/Web/Request/Builder/HttpRequestMessageBuilderExtension.cs +++ b/src/Snap.Hutao/Snap.Hutao/Web/Request/Builder/HttpRequestMessageBuilderExtension.cs @@ -1,11 +1,14 @@ // Copyright (c) DGP Studio. All rights reserved. // Licensed under the MIT license. +using Snap.Hutao.Service.Notification; using Snap.Hutao.Web.Hutao.Response; +using Snap.Hutao.Web.Response; using System.IO; using System.Net; using System.Net.Http; using System.Net.Sockets; +using System.Text; namespace Snap.Hutao.Web.Request.Builder; @@ -22,6 +25,10 @@ internal static class HttpRequestMessageBuilderExtension internal static async ValueTask SendAsync(this HttpRequestMessageBuilder builder, HttpClient httpClient, ILogger logger, CancellationToken token) where TResult : class { + StringBuilder messageBuilder = new(); + messageBuilder.AppendLine(System.Globalization.CultureInfo.CurrentCulture, $"Host: {builder.RequestUri?.Host}"); + bool showInfo = true; + try { using (builder.HttpRequestMessage) @@ -29,51 +36,50 @@ internal static class HttpRequestMessageBuilderExtension using (HttpResponseMessage message = await httpClient.SendAsync(builder.HttpRequestMessage, token).ConfigureAwait(false)) { message.EnsureSuccessStatusCode(); - return await builder.HttpContentSerializer.DeserializeAsync(message.Content, token).ConfigureAwait(false); + showInfo = false; + return result = await builder.HttpContentSerializer.DeserializeAsync(message.Content, token).ConfigureAwait(false); } } } catch (HttpRequestException ex) { - logger.LogWarning(ex, RequestErrorMessage, builder.HttpRequestMessage.RequestUri); - - if (ex.StatusCode is HttpStatusCode.BadGateway) + if (TryHandleHttp502HutaoResponseSpecialCase(ex, out TResult? result)) { - Type resultType = typeof(TResult); - - if (resultType == typeof(HutaoResponse)) - { - return Activator.CreateInstance(resultType, 502, SH.WebHutaoServiceUnAvailable, default) as TResult; - } - - if (resultType.IsConstructedGenericType && resultType.GetGenericTypeDefinition() == typeof(HutaoResponse<>)) - { - return Activator.CreateInstance(resultType, 502, SH.WebHutaoServiceUnAvailable, default, default) as TResult; - } + return result; } - return default; + ProcessException(messageBuilder, ex); + logger.LogWarning(ex, RequestErrorMessage, builder.RequestUri); } catch (IOException ex) { - logger.LogWarning(ex, RequestErrorMessage, builder.HttpRequestMessage.RequestUri); - return default; + ProcessException(messageBuilder, ex); + logger.LogWarning(ex, RequestErrorMessage, builder.RequestUri); } catch (JsonException ex) { - logger.LogWarning(ex, RequestErrorMessage, builder.HttpRequestMessage.RequestUri); - return default; + ProcessException(messageBuilder, ex); + logger.LogWarning(ex, RequestErrorMessage, builder.RequestUri); } catch (HttpContentSerializationException ex) { - logger.LogWarning(ex, RequestErrorMessage, builder.HttpRequestMessage.RequestUri); - return default; + ProcessException(messageBuilder, ex); + logger.LogWarning(ex, RequestErrorMessage, builder.RequestUri); } catch (SocketException ex) { - logger.LogWarning(ex, RequestErrorMessage, builder.HttpRequestMessage.RequestUri); - return default; + ProcessException(messageBuilder, ex); + logger.LogWarning(ex, RequestErrorMessage, builder.RequestUri); } + finally + { + if (showInfo) + { + builder.ServiceProvider.GetRequiredService().Error(messageBuilder.ToString()); + } + } + + return default; } internal static void Send(this HttpRequestMessageBuilder builder, HttpClient httpClient, ILogger logger) @@ -86,23 +92,95 @@ internal static class HttpRequestMessageBuilderExtension } catch (HttpRequestException ex) { - logger.LogWarning(ex, RequestErrorMessage, builder.HttpRequestMessage.RequestUri); + logger.LogWarning(ex, RequestErrorMessage, builder.RequestUri); } catch (IOException ex) { - logger.LogWarning(ex, RequestErrorMessage, builder.HttpRequestMessage.RequestUri); + logger.LogWarning(ex, RequestErrorMessage, builder.RequestUri); } catch (JsonException ex) { - logger.LogWarning(ex, RequestErrorMessage, builder.HttpRequestMessage.RequestUri); + logger.LogWarning(ex, RequestErrorMessage, builder.RequestUri); } catch (HttpContentSerializationException ex) { - logger.LogWarning(ex, RequestErrorMessage, builder.HttpRequestMessage.RequestUri); + logger.LogWarning(ex, RequestErrorMessage, builder.RequestUri); } catch (SocketException ex) { - logger.LogWarning(ex, RequestErrorMessage, builder.HttpRequestMessage.RequestUri); + logger.LogWarning(ex, RequestErrorMessage, builder.RequestUri); + } + } + + private static bool TryHandleHttp502HutaoResponseSpecialCase(HttpRequestException ex, out TResult? result) + where TResult : class + { + result = default; + + if (ex.StatusCode is HttpStatusCode.BadGateway) + { + Type resultType = typeof(TResult); + + if (resultType == typeof(HutaoResponse)) + { + // HutaoResponse(int returnCode, string message, string? localizationKey) + result = Activator.CreateInstance(resultType, 502, SH.WebHutaoServiceUnAvailable, default) as TResult; + return true; + } + + if (resultType.IsConstructedGenericType && resultType.GetGenericTypeDefinition() == typeof(HutaoResponse<>)) + { + // HutaoResponse(int returnCode, string message, TData? data, string? localizationKey) + result = Activator.CreateInstance(resultType, 502, SH.WebHutaoServiceUnAvailable, default, default) as TResult; + return true; + } + } + + return false; + } + + [SuppressMessage("", "CA1305")] + private static void ProcessException(StringBuilder builder, Exception exception) + { + if (exception is HttpRequestException hre) + { + builder + .AppendLine($"{nameof(HttpRequestException)}: Status Code: {hre.StatusCode} Error: {hre.HttpRequestError}") + .AppendLine(hre.Message); + } + + if (exception is IOException ioe) + { + builder + .AppendLine($"{nameof(IOException)}: 0x{ioe.HResult:X8}") + .AppendLine(ioe.Message); + } + + if (exception is JsonException je) + { + builder + .AppendLine($"{nameof(JsonException)}: Path: {je.Path} at Line: {je.LineNumber} Position: {je.BytePositionInLine}") + .AppendLine(je.Message); + } + + if (exception is HttpContentSerializationException hcse) + { + builder + .AppendLine($"{nameof(HttpContentSerializationException)}:") + .AppendLine(hcse.Message); + } + + if (exception is SocketException se) + { + builder + .AppendLine($"{nameof(SocketException)}: Error: {se.SocketErrorCode}") + .AppendLine(se.Message); + } + + if (exception.InnerException is { } inner) + { + builder.AppendLine(new string('-', 40)); + ProcessException(builder, inner); } } } \ No newline at end of file diff --git a/src/Snap.Hutao/Snap.Hutao/Web/Request/Builder/HttpRequestMessageBuilderFactory.cs b/src/Snap.Hutao/Snap.Hutao/Web/Request/Builder/HttpRequestMessageBuilderFactory.cs index e6465871..6385dcc1 100644 --- a/src/Snap.Hutao/Snap.Hutao/Web/Request/Builder/HttpRequestMessageBuilderFactory.cs +++ b/src/Snap.Hutao/Snap.Hutao/Web/Request/Builder/HttpRequestMessageBuilderFactory.cs @@ -10,9 +10,10 @@ namespace Snap.Hutao.Web.Request.Builder; internal sealed partial class HttpRequestMessageBuilderFactory : IHttpRequestMessageBuilderFactory { private readonly JsonHttpContentSerializer jsonHttpContentSerializer; + private readonly IServiceProvider serviceProvider; public HttpRequestMessageBuilder Create() { - return new(jsonHttpContentSerializer); + return new(serviceProvider, jsonHttpContentSerializer); } } \ No newline at end of file