diff --git a/src/BootstrapBlazor.Server/BootstrapBlazor.Server.csproj b/src/BootstrapBlazor.Server/BootstrapBlazor.Server.csproj index 9e9a9b32164..4f7700e9aba 100644 --- a/src/BootstrapBlazor.Server/BootstrapBlazor.Server.csproj +++ b/src/BootstrapBlazor.Server/BootstrapBlazor.Server.csproj @@ -42,6 +42,7 @@ + diff --git a/src/BootstrapBlazor.Server/Extensions/ServiceCollectionSharedExtensions.cs b/src/BootstrapBlazor.Server/Extensions/ServiceCollectionSharedExtensions.cs index 479c7981f93..d34dd4a48df 100644 --- a/src/BootstrapBlazor.Server/Extensions/ServiceCollectionSharedExtensions.cs +++ b/src/BootstrapBlazor.Server/Extensions/ServiceCollectionSharedExtensions.cs @@ -82,6 +82,9 @@ public static IServiceCollection AddBootstrapBlazorServices(this IServiceCollect // 增加离线 IP 定位服务 services.AddBootstrapBlazorIP2RegionfService(); + // 增加 JuHe 定位服务 + services.AddBootstrapBlazorJuHeIpLocatorService(); + // 增加 PetaPoco ORM 数据服务操作类 // 需要时打开下面代码 //services.AddPetaPoco(option => diff --git a/src/BootstrapBlazor.Server/Locales/en-US.json b/src/BootstrapBlazor.Server/Locales/en-US.json index fae7a9e2562..e1dae3769ac 100644 --- a/src/BootstrapBlazor.Server/Locales/en-US.json +++ b/src/BootstrapBlazor.Server/Locales/en-US.json @@ -4142,7 +4142,7 @@ "LocatorsNormalInputText": "IpAddress", "LocatorsNormalDisplayText": "Geographical location", "LocatorsNormalButtonText": "Locating", - "LocatorsProviderDesc": "

The component library has two built-in free online geolocation locators, BaiduIpLocatorProvider BaiduIpLocatorProviderV2

The component library has a built-in paid online geolocation locator JuHeIpLocatorProvider Official website address

The component library has a built-in free offline geolocation locator BootstrapBlazor.IP2Region Nuget package

" + "LocatorsProviderDesc": "

The component library has two built-in free online geolocation locators, BaiduIpLocatorProvider BaiduIpLocatorProviderV2

The component library has a built-in paid online geolocation locator BootstrapBlazor.JuHeIpLocatorProvider Nuget package Official website address

The component library has a built-in free offline geolocation locator BootstrapBlazor.IP2Region Nuget package

" }, "BootstrapBlazor.Server.Components.Samples.Print": { "PrintsTitle": "Print", diff --git a/src/BootstrapBlazor.Server/Locales/zh-CN.json b/src/BootstrapBlazor.Server/Locales/zh-CN.json index dbbb087a331..1a56417b3b0 100644 --- a/src/BootstrapBlazor.Server/Locales/zh-CN.json +++ b/src/BootstrapBlazor.Server/Locales/zh-CN.json @@ -4142,7 +4142,7 @@ "LocatorsNormalInputText": "Ip 地址", "LocatorsNormalDisplayText": "地理位置", "LocatorsNormalButtonText": "定位", - "LocatorsProviderDesc": "

组件库内置两个免费在线地理位置定位器分别为 BaiduIpLocatorProvider BaiduIpLocatorProviderV2

组件库内置一个收费在线地理位置定位器 JuHeIpLocatorProvider 官网地址

组件库内置一个免费离线地理位置定位器 BootstrapBlazor.IP2Region Nuget 包

" + "LocatorsProviderDesc": "

组件库内置两个免费在线地理位置定位器分别为 BaiduIpLocatorProvider BaiduIpLocatorProviderV2

组件库内置一个收费在线地理位置定位器 BootstrapBlazor.JuHeIpLocatorProvider Nuget 包 官网地址

组件库内置一个免费离线地理位置定位器 BootstrapBlazor.IP2Region Nuget 包

" }, "BootstrapBlazor.Server.Components.Samples.Print": { "PrintsTitle": "Print 打印按钮", diff --git a/src/BootstrapBlazor/BootstrapBlazor.csproj b/src/BootstrapBlazor/BootstrapBlazor.csproj index 86ee6197a7c..6cb35269ddc 100644 --- a/src/BootstrapBlazor/BootstrapBlazor.csproj +++ b/src/BootstrapBlazor/BootstrapBlazor.csproj @@ -1,7 +1,7 @@ - 9.3.1-beta23 + 9.3.1-beta24 diff --git a/src/BootstrapBlazor/Extensions/BootstrapBlazorServiceCollectionExtensions.cs b/src/BootstrapBlazor/Extensions/BootstrapBlazorServiceCollectionExtensions.cs index 163f66f6756..12af4f4ac80 100644 --- a/src/BootstrapBlazor/Extensions/BootstrapBlazorServiceCollectionExtensions.cs +++ b/src/BootstrapBlazor/Extensions/BootstrapBlazorServiceCollectionExtensions.cs @@ -47,10 +47,14 @@ public static IServiceCollection AddBootstrapBlazor(this IServiceCollection serv // IP 地理位置定位服务 services.TryAddSingleton(); - services.AddSingleton(); services.AddSingleton(); services.AddSingleton(); +#if NET8_0_OR_GREATER + services.AddKeyedSingleton(nameof(BaiduIpLocatorProvider)); + services.AddKeyedSingleton(nameof(BaiduIpLocatorProviderV2)); +#endif + // 节日服务 services.TryAddSingleton(); diff --git a/src/BootstrapBlazor/Services/IPLocator/JuHeIpLocatorProvider.cs b/src/BootstrapBlazor/Services/IPLocator/JuHeIpLocatorProvider.cs deleted file mode 100644 index 7a6dcab3943..00000000000 --- a/src/BootstrapBlazor/Services/IPLocator/JuHeIpLocatorProvider.cs +++ /dev/null @@ -1,203 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License -// See the LICENSE file in the project root for more information. -// Maintainer: Argo Zhang(argo@live.ca) Website: https://www.blazor.zone - -using Microsoft.Extensions.Configuration; -using Microsoft.Extensions.Logging; -using System.Net.Http.Json; -using System.Text.Json.Serialization; - -namespace BootstrapBlazor.Components; - -/// -/// 聚合搜索引擎 IP 定位器 -/// -/// -/// -/// -/// -public class JuHeIpLocatorProvider(IHttpClientFactory httpClientFactory, IConfiguration configuration, IOptions options, ILogger logger) : DefaultIpLocatorProvider(options) -{ - private HttpClient? _client; - - private string? _key; - - /// - /// - /// - /// - protected override async Task LocateByIp(string ip) - { - string? ret = null; - _key ??= GetKey(); - var url = $"https://apis.juhe.cn/iplocation/ipv4dist/query?ip={ip}&key={_key}"; - try - { - _client ??= httpClientFactory.CreateClient(); - using var token = new CancellationTokenSource(3000); - ret = await Fetch(url, _client, token.Token); - } - catch (Exception ex) - { - logger.LogError(ex, "Url: {url}", url); - } - return ret; - } - - private string GetKey() - { - var ret = configuration["JuHe:IpLocatorKey"]; - if (string.IsNullOrEmpty(ret)) - { - throw new InvalidOperationException("JuHe:IpLocatorKey not value in appsettings configuration file. 未配置 JuHe:IpLocatorKey 请在 appsettings.json 中配置 JuHe:IpLocatorKey"); - } - return ret; - } - - /// - /// 请求获得地理位置接口方法 - /// - /// - /// - /// - /// - protected virtual async Task Fetch(string url, HttpClient client, CancellationToken token) - { - var result = await client.GetFromJsonAsync(url, token); - if (result != null && result.ErrorCode != 0) - { - logger.LogError("ErrorCode: {ErrorCode} Reason: {Reason}", result.ErrorCode, result.Reason); - } - return result?.ToString(); - } - - /// - /// LocationResult 结构体 - /// - [ExcludeFromCodeCoverage] - class LocationResult - { - /// - /// 获得/设置 结果状态返回码 为 查询成功 时通讯正常 - /// - public string? Reason { get; set; } - - /// - /// 获得/设置 错误码 - /// - [JsonPropertyName("error_code")] - public int ErrorCode { get; set; } - - /// - /// 获得/设置 定位信息 - /// - public LocationData? Result { get; set; } - - /// - /// - /// - /// - public override string? ToString() - { - string? ret = null; - if (ErrorCode == 0) - { - ret = Result?.Country == "中国" - ? $"{Result?.Prov}{Result?.City}{Result?.District} {Result?.Isp}" - : $"{Result?.Continent} {Result?.Country} {Result?.City}"; - } - return ret; - } - } - - - [ExcludeFromCodeCoverage] - class LocationData - { - /// - /// 获得/设置 州 - /// - public string? Continent { get; set; } - - /// - /// 获得/设置 国家 - /// - public string? Country { get; set; } - - /// - /// 获得/设置 邮编 - /// - public string? ZipCode { get; set; } - - /// - /// 获得/设置 时区 - /// - public string? TimeZone { get; set; } - - /// - /// 获得/设置 精度 - /// - public string? Accuracy { get; set; } - - /// - /// 获得/设置 所属 - /// - public string? Owner { get; set; } - - /// - /// 获得/设置 运营商 - /// - public string? Isp { get; set; } - - /// - /// 获得/设置 来源 - /// - public string? Source { get; set; } - - /// - /// 获得/设置 区号 - /// - public string? AreaCode { get; set; } - - /// - /// 获得/设置 行政区划代码 - /// - public string? AdCode { get; set; } - - /// - /// 获得/设置 国家代码 - /// - public string? AsNumber { get; set; } - - /// - /// 获得/设置 经度 - /// - public string? Lat { get; set; } - - /// - /// 获得/设置 纬度 - /// - public string? Lng { get; set; } - - /// - /// 获得/设置 半径 - /// - public string? Radius { get; set; } - - /// - /// 获得/设置 省份 - /// - public string? Prov { get; set; } - - /// - /// 获得/设置 城市 - /// - public string? City { get; set; } - - /// - /// 获得/设置 区县 - /// - public string? District { get; set; } - } -} diff --git a/test/UnitTest/Services/IpLocatorTest.cs b/test/UnitTest/Services/IpLocatorTest.cs index c4aab8cdc5d..bc3ebd9b04e 100644 --- a/test/UnitTest/Services/IpLocatorTest.cs +++ b/test/UnitTest/Services/IpLocatorTest.cs @@ -61,34 +61,16 @@ public async Task BaiduIpLocatorProviderV2_Ok() Assert.Equal("省份城市区县 测试", result); } - [Fact] - public async Task JuHeIpLocatorProvider_Ok() - { - var factory = Context.Services.GetRequiredService(); - var option = Context.Services.GetRequiredService>(); - var logger = Context.Services.GetRequiredService>(); - var configuration = Context.Services.GetRequiredService(); - var provider = new MockJuHeProvider(factory, configuration, option, logger); - - var result = await provider.Locate("127.0.0.1"); - Assert.Equal("localhost", result); - - result = await provider.Locate(""); - Assert.Equal("localhost", result); - - // 河南省漯河市 移动 - result = await provider.Locate("223.91.188.112"); - Assert.Equal("省份城市区县 测试", result); - } - [Fact] public void Factory_Ok() { var factory = Context.Services.GetRequiredService(); Assert.NotNull(factory.Create("BaiduIpLocatorProviderV2")); Assert.NotNull(factory.Create("BaiduIpLocatorProvider")); - Assert.NotNull(factory.Create("JuHeIpLocatorProvider")); Assert.NotNull(factory.Create()); + + Assert.NotNull(Context.Services.GetKeyedService("BaiduIpLocatorProvider")); + Assert.NotNull(Context.Services.GetKeyedService("BaiduIpLocatorProviderV2")); } [Fact] @@ -111,12 +93,6 @@ public async Task Fetch_Error() var cancelPprovider = new MockProviderFetchCancelError(factory, option, logger); result = await cancelPprovider.Locate("223.91.188.112"); Assert.Null(result); - - var configuration = Context.Services.GetRequiredService(); - var juHeLogger = Context.Services.GetRequiredService>(); - var juHeProvider = new MockProviderJuHeFetchError(factory, configuration, option, juHeLogger); - result = await juHeProvider.Locate("223.91.188.112"); - Assert.Null(result); } [Fact] @@ -134,32 +110,6 @@ public async Task Fetch_Result_Fail() var providerV2 = new MockBaiduProviderV2(factory, option, loggerV2); result = await providerV2.Locate("223.91.188.112"); Assert.Null(result); - - var configuration = Context.Services.GetRequiredService(); - var loggerJuHe = Context.Services.GetRequiredService>(); - var providerJuHe = new MockJuHeNullProvider(factory, configuration, option, loggerJuHe); - result = await providerJuHe.Locate("223.91.188.112"); - Assert.Null(result); - - var providerJuHeFailed = new MockJuHeFailProvider(factory, configuration, option, loggerJuHe); - result = await providerJuHeFailed.Locate("223.91.188.112"); - Assert.Null(result); - } - - [Fact] - public async Task JuHe_Section_Error() - { - var factory = Context.Services.GetRequiredService(); - var option = Context.Services.GetRequiredService>(); - var logger = Context.Services.GetRequiredService>(); - var builder = new ConfigurationBuilder(); - builder.AddInMemoryCollection(new Dictionary - { - ["JuHe:IpLocatorKey"] = "" - }); - var configuration = builder.Build(); - var provider = new MockJuHeNullProvider(factory, configuration, option, logger); - await Assert.ThrowsAsync(async () => await provider.Locate("223.91.188.112")); } class MockProviderFetchError(IHttpClientFactory httpClientFactory, IOptions option, ILogger logger) : BaiduIpLocatorProvider(httpClientFactory, option, logger) @@ -172,11 +122,6 @@ class MockProviderFetchCancelError(IHttpClientFactory httpClientFactory, IOption protected override Task Fetch(string url, HttpClient client, CancellationToken token) => throw new TaskCanceledException(); } - class MockProviderJuHeFetchError(IHttpClientFactory httpClientFactory, IConfiguration configuration, IOptions option, ILogger logger) : JuHeIpLocatorProvider(httpClientFactory, configuration, option, logger) - { - protected override Task Fetch(string url, HttpClient client, CancellationToken token) => throw new InvalidOperationException(); - } - class MockBaiduProvider(IHttpClientFactory httpClientFactory, IOptions option, ILogger logger) : BaiduIpLocatorProvider(httpClientFactory, option, logger) { protected override Task Fetch(string url, HttpClient client, CancellationToken token) @@ -195,24 +140,6 @@ class MockBaiduProviderV2(IHttpClientFactory httpClientFactory, IOptions option, ILogger logger) : JuHeIpLocatorProvider(httpClientFactory, configuration, option, logger) - { - protected override Task Fetch(string url, HttpClient client, CancellationToken token) - { - client = new HttpClient(new MockHttpNullMessageHandler(), true); - return base.Fetch(url, client, token); - } - } - - class MockJuHeFailProvider(IHttpClientFactory httpClientFactory, IConfiguration configuration, IOptions option, ILogger logger) : JuHeIpLocatorProvider(httpClientFactory, configuration, option, logger) - { - protected override Task Fetch(string url, HttpClient client, CancellationToken token) - { - client = new HttpClient(new MockHttpFailedMessageHandlerJuHe(), true); - return base.Fetch(url, client, token); - } - } - class MockProvider(IHttpClientFactory httpClientFactory, IOptions option, ILogger logger) : BaiduIpLocatorProvider(httpClientFactory, option, logger) { protected override Task Fetch(string url, HttpClient client, CancellationToken token) @@ -231,24 +158,6 @@ class MockProviderV2(IHttpClientFactory httpClientFactory, IOptions option, ILogger logger) : JuHeIpLocatorProvider(httpClientFactory, configuration, option, logger) - { - protected override Task Fetch(string url, HttpClient client, CancellationToken token) - { - client = new HttpClient(new MockHttpSuccessMessageHandlerJuHe(), true); - return base.Fetch(url, client, token); - } - } - - class MockJuHeFailedProvider(IHttpClientFactory httpClientFactory, IConfiguration configuration, IOptions option, ILogger logger) : JuHeIpLocatorProvider(httpClientFactory, configuration, option, logger) - { - protected override Task Fetch(string url, HttpClient client, CancellationToken token) - { - client = new HttpClient(new MockHttpFailedMessageHandlerJuHe(), true); - return base.Fetch(url, client, token); - } - } - class MockHttpNullMessageHandler : HttpMessageHandler { protected override Task SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)