From 029db2b112985bbcb62eab28cb12f13a55af3ea2 Mon Sep 17 00:00:00 2001 From: maliming Date: Wed, 11 Jun 2025 14:10:58 +0800 Subject: [PATCH] Use `MyCSharp.HttpUserAgentParser` to replace `DeviceDetector.NET` for better performance. --- Directory.Packages.props | 2 +- ...ltAbpRequestLocalizationOptionsProvider.cs | 1 - .../Volo.Abp.AspNetCore.csproj | 2 +- .../Abp/AspNetCore/AbpAspNetCoreModule.cs | 3 ++ .../HttpContextWebClientInfoProvider.cs | 36 +++++++++---------- .../Mvc/FakeAuthenticationScheme.cs | 2 +- .../WebClientInfoProviderDto.cs | 8 +++++ .../WebClientInfoProviderTestController.cs | 26 ++++++++++++++ ...bClientInfoProviderTestController_Tests.cs | 36 +++++++++++++++++++ 9 files changed, 93 insertions(+), 23 deletions(-) create mode 100644 framework/test/Volo.Abp.AspNetCore.Mvc.Tests/Volo/Abp/AspNetCore/Mvc/WebClientInfoProvider/WebClientInfoProviderDto.cs create mode 100644 framework/test/Volo.Abp.AspNetCore.Mvc.Tests/Volo/Abp/AspNetCore/Mvc/WebClientInfoProvider/WebClientInfoProviderTestController.cs create mode 100644 framework/test/Volo.Abp.AspNetCore.Mvc.Tests/Volo/Abp/AspNetCore/Mvc/WebClientInfoProvider/WebClientInfoProviderTestController_Tests.cs diff --git a/Directory.Packages.props b/Directory.Packages.props index 72405c55cf..e74fc60a72 100644 --- a/Directory.Packages.props +++ b/Directory.Packages.props @@ -29,7 +29,7 @@ - + diff --git a/framework/src/Volo.Abp.AspNetCore/Microsoft/AspNetCore/RequestLocalization/DefaultAbpRequestLocalizationOptionsProvider.cs b/framework/src/Volo.Abp.AspNetCore/Microsoft/AspNetCore/RequestLocalization/DefaultAbpRequestLocalizationOptionsProvider.cs index bd767f93fb..cf9f128852 100644 --- a/framework/src/Volo.Abp.AspNetCore/Microsoft/AspNetCore/RequestLocalization/DefaultAbpRequestLocalizationOptionsProvider.cs +++ b/framework/src/Volo.Abp.AspNetCore/Microsoft/AspNetCore/RequestLocalization/DefaultAbpRequestLocalizationOptionsProvider.cs @@ -4,7 +4,6 @@ using System.Globalization; using System.Linq; using System.Threading; using System.Threading.Tasks; -using DeviceDetectorNET; using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Localization; using Microsoft.Extensions.DependencyInjection; diff --git a/framework/src/Volo.Abp.AspNetCore/Volo.Abp.AspNetCore.csproj b/framework/src/Volo.Abp.AspNetCore/Volo.Abp.AspNetCore.csproj index d88febbfe9..886e1ed700 100644 --- a/framework/src/Volo.Abp.AspNetCore/Volo.Abp.AspNetCore.csproj +++ b/framework/src/Volo.Abp.AspNetCore/Volo.Abp.AspNetCore.csproj @@ -31,7 +31,7 @@ - + diff --git a/framework/src/Volo.Abp.AspNetCore/Volo/Abp/AspNetCore/AbpAspNetCoreModule.cs b/framework/src/Volo.Abp.AspNetCore/Volo/Abp/AspNetCore/AbpAspNetCoreModule.cs index 64fab10ac6..405e49753c 100644 --- a/framework/src/Volo.Abp.AspNetCore/Volo/Abp/AspNetCore/AbpAspNetCoreModule.cs +++ b/framework/src/Volo.Abp.AspNetCore/Volo/Abp/AspNetCore/AbpAspNetCoreModule.cs @@ -4,6 +4,7 @@ using Microsoft.AspNetCore.Hosting.StaticWebAssets; using Microsoft.AspNetCore.RequestLocalization; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.FileProviders; +using MyCSharp.HttpUserAgentParser.DependencyInjection; using Volo.Abp.AspNetCore.Auditing; using Volo.Abp.AspNetCore.VirtualFileSystem; using Volo.Abp.Auditing; @@ -59,6 +60,8 @@ public class AbpAspNetCoreModule : AbpModule context.Services.AddAbpDynamicOptions(); StaticWebAssetsLoader.UseStaticWebAssets(context.Services.GetHostingEnvironment(), context.Services.GetConfiguration()); + + context.Services.AddHttpUserAgentCachedParser(); } private static void AddAspNetServices(IServiceCollection services) diff --git a/framework/src/Volo.Abp.AspNetCore/Volo/Abp/AspNetCore/WebClientInfo/HttpContextWebClientInfoProvider.cs b/framework/src/Volo.Abp.AspNetCore/Volo/Abp/AspNetCore/WebClientInfo/HttpContextWebClientInfoProvider.cs index 6e450efbcd..b34a9752f7 100644 --- a/framework/src/Volo.Abp.AspNetCore/Volo/Abp/AspNetCore/WebClientInfo/HttpContextWebClientInfoProvider.cs +++ b/framework/src/Volo.Abp.AspNetCore/Volo/Abp/AspNetCore/WebClientInfo/HttpContextWebClientInfoProvider.cs @@ -1,7 +1,8 @@ using System; -using DeviceDetectorNET; using Microsoft.AspNetCore.Http; using Microsoft.Extensions.Logging; +using MyCSharp.HttpUserAgentParser; +using MyCSharp.HttpUserAgentParser.Providers; using Volo.Abp.DependencyInjection; namespace Volo.Abp.AspNetCore.WebClientInfo; @@ -11,13 +12,16 @@ public class HttpContextWebClientInfoProvider : IWebClientInfoProvider, ITransie { protected ILogger Logger { get; } protected IHttpContextAccessor HttpContextAccessor { get; } + protected IHttpUserAgentParserProvider HttpUserAgentParser { get; } public HttpContextWebClientInfoProvider( ILogger logger, - IHttpContextAccessor httpContextAccessor) + IHttpContextAccessor httpContextAccessor, + IHttpUserAgentParserProvider httpUserAgentParser) { Logger = logger; HttpContextAccessor = httpContextAccessor; + HttpUserAgentParser = httpUserAgentParser; } public string? BrowserInfo => GetBrowserInfo(); @@ -46,27 +50,21 @@ public class HttpContextWebClientInfoProvider : IWebClientInfoProvider, ITransie protected virtual string? GetDeviceInfo() { - string? deviceInfo = null; - var deviceDetector = new DeviceDetector(GetBrowserInfo()); - deviceDetector.Parse(); - if (!deviceDetector.IsParsed()) + var browserInfo = GetBrowserInfo(); + if (browserInfo.IsNullOrWhiteSpace()) { - return deviceInfo; - } - - var osInfo = deviceDetector.GetOs(); - if (osInfo.Success) - { - deviceInfo = osInfo.Match.Name; + return null; } - var clientInfo = deviceDetector.GetClient(); - if (clientInfo.Success) + var httpUserAgentInformation = HttpUserAgentParser.Parse(browserInfo); + switch (httpUserAgentInformation.Type) { - deviceInfo = deviceInfo.IsNullOrWhiteSpace() ? clientInfo.Match.Name : deviceInfo + " " + clientInfo.Match.Name; + case HttpUserAgentType.Browser: + case HttpUserAgentType.Robot: + return (httpUserAgentInformation.Platform.HasValue ? httpUserAgentInformation.Platform.Value.Name + " " : string.Empty) + httpUserAgentInformation.Name; + case HttpUserAgentType.Unknown: + default: + return httpUserAgentInformation.UserAgent; } - - return deviceInfo; } - } diff --git a/framework/test/Volo.Abp.AspNetCore.Mvc.Tests/Volo/Abp/AspNetCore/Mvc/FakeAuthenticationScheme.cs b/framework/test/Volo.Abp.AspNetCore.Mvc.Tests/Volo/Abp/AspNetCore/Mvc/FakeAuthenticationScheme.cs index 641bbe03d7..0f9da48f68 100644 --- a/framework/test/Volo.Abp.AspNetCore.Mvc.Tests/Volo/Abp/AspNetCore/Mvc/FakeAuthenticationScheme.cs +++ b/framework/test/Volo.Abp.AspNetCore.Mvc.Tests/Volo/Abp/AspNetCore/Mvc/FakeAuthenticationScheme.cs @@ -1,8 +1,8 @@ using System; +using System.Linq; using System.Security.Claims; using System.Text.Encodings.Web; using System.Threading.Tasks; -using DeviceDetectorNET; using Microsoft.AspNetCore.Authentication; using Microsoft.Extensions.Logging; using Microsoft.Extensions.Options; diff --git a/framework/test/Volo.Abp.AspNetCore.Mvc.Tests/Volo/Abp/AspNetCore/Mvc/WebClientInfoProvider/WebClientInfoProviderDto.cs b/framework/test/Volo.Abp.AspNetCore.Mvc.Tests/Volo/Abp/AspNetCore/Mvc/WebClientInfoProvider/WebClientInfoProviderDto.cs new file mode 100644 index 0000000000..85f96a65e3 --- /dev/null +++ b/framework/test/Volo.Abp.AspNetCore.Mvc.Tests/Volo/Abp/AspNetCore/Mvc/WebClientInfoProvider/WebClientInfoProviderDto.cs @@ -0,0 +1,8 @@ +namespace Volo.Abp.AspNetCore.Mvc.WebClientInfoProvider; + +public class WebClientInfoProviderDto +{ + public string BrowserInfo { get; set; } + + public string DeviceInfo { get; set; } +} diff --git a/framework/test/Volo.Abp.AspNetCore.Mvc.Tests/Volo/Abp/AspNetCore/Mvc/WebClientInfoProvider/WebClientInfoProviderTestController.cs b/framework/test/Volo.Abp.AspNetCore.Mvc.Tests/Volo/Abp/AspNetCore/Mvc/WebClientInfoProvider/WebClientInfoProviderTestController.cs new file mode 100644 index 0000000000..0d09c6e8ee --- /dev/null +++ b/framework/test/Volo.Abp.AspNetCore.Mvc.Tests/Volo/Abp/AspNetCore/Mvc/WebClientInfoProvider/WebClientInfoProviderTestController.cs @@ -0,0 +1,26 @@ +using System.Threading.Tasks; +using Microsoft.AspNetCore.Mvc; +using Volo.Abp.AspNetCore.WebClientInfo; + +namespace Volo.Abp.AspNetCore.Mvc.WebClientInfoProvider; + +[Route("api/web-client-info")] +public class WebClientInfoProviderTestController : AbpController +{ + private IWebClientInfoProvider _webClientInfoProvider { get; } + + public WebClientInfoProviderTestController(IWebClientInfoProvider webClientInfoProvider) + { + _webClientInfoProvider = webClientInfoProvider; + } + + [HttpGet] + public Task GetAsync() + { + return Task.FromResult(new WebClientInfoProviderDto + { + BrowserInfo = _webClientInfoProvider.BrowserInfo, + DeviceInfo = _webClientInfoProvider.DeviceInfo + }); + } +} diff --git a/framework/test/Volo.Abp.AspNetCore.Mvc.Tests/Volo/Abp/AspNetCore/Mvc/WebClientInfoProvider/WebClientInfoProviderTestController_Tests.cs b/framework/test/Volo.Abp.AspNetCore.Mvc.Tests/Volo/Abp/AspNetCore/Mvc/WebClientInfoProvider/WebClientInfoProviderTestController_Tests.cs new file mode 100644 index 0000000000..0a5f13082b --- /dev/null +++ b/framework/test/Volo.Abp.AspNetCore.Mvc.Tests/Volo/Abp/AspNetCore/Mvc/WebClientInfoProvider/WebClientInfoProviderTestController_Tests.cs @@ -0,0 +1,36 @@ +using System.Net; +using System.Net.Http; +using System.Text.Json; +using System.Threading.Tasks; +using Shouldly; +using Xunit; + +namespace Volo.Abp.AspNetCore.Mvc.WebClientInfoProvider; + +public class WebClientInfoProviderTestController_Tests : AspNetCoreMvcTestBase +{ + [Theory] + [InlineData("Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/137.0.0.0 Safari/537.36", "Windows 10 Chrome")] + [InlineData("Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/137.0.0.0 Safari/537.36", "Mac OS X Chrome")] + [InlineData("PostmanRuntime/7.43.4", "PostmanRuntime/7.43.4")] + [InlineData("curl/7.64.1", "curl/7.64.1")] + [InlineData("Mozilla/5.0 (compatible; MojeekBot/0.11; +mojeek.com/bot.html)", "MojeekBot")] + public async Task TestAsync(string ua, string device) + { + var clientInfo = await GetWebClientInfoAsync(ua); + clientInfo.ShouldNotBeNull(); + clientInfo.BrowserInfo.ShouldBe(ua); + clientInfo.DeviceInfo.ShouldBe(device); + } + + private async Task GetWebClientInfoAsync(string userAgent ) + { + using (var requestMessage = new HttpRequestMessage(HttpMethod.Get, "api/web-client-info")) + { + requestMessage.Headers.Add("User-Agent", userAgent); + var response = await Client.SendAsync(requestMessage); + response.StatusCode.ShouldBe(HttpStatusCode.OK); + return JsonSerializer.Deserialize(await response.Content.ReadAsStringAsync(), JsonSerializerOptions.Web); + } + } +}