Skip to content

Commit a76e457

Browse files
authored
feat(JuHeIpLocatorProvider): add BootstrapBlazor.JuHeIpLocatorProvider project (#323)
* feat: 增加 JuHeIpLocatorProvider 工程 * chore: 增加工程
1 parent eb4064f commit a76e457

5 files changed

Lines changed: 293 additions & 0 deletions

File tree

BootstrapBlazor.Extensions.sln

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -172,6 +172,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "BootstrapBlazor.OctIcon", "
172172
EndProject
173173
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "BootstrapBlazor.IP2Region", "src\components\BootstrapBlazor.IP2Region\BootstrapBlazor.IP2Region.csproj", "{8A46FEDF-FD76-48AB-9BB2-47254C7623A4}"
174174
EndProject
175+
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "BootstrapBlazor.JuHeIpLocatorProvider", "src\components\BootstrapBlazor.JuHeIpLocatorProvider\BootstrapBlazor.JuHeIpLocatorProvider.csproj", "{C882891F-4A6A-C9BE-AC96-227764A4A46A}"
176+
EndProject
175177
Global
176178
GlobalSection(SolutionConfigurationPlatforms) = preSolution
177179
Debug|Any CPU = Debug|Any CPU
@@ -462,6 +464,10 @@ Global
462464
{8A46FEDF-FD76-48AB-9BB2-47254C7623A4}.Debug|Any CPU.Build.0 = Debug|Any CPU
463465
{8A46FEDF-FD76-48AB-9BB2-47254C7623A4}.Release|Any CPU.ActiveCfg = Release|Any CPU
464466
{8A46FEDF-FD76-48AB-9BB2-47254C7623A4}.Release|Any CPU.Build.0 = Release|Any CPU
467+
{C882891F-4A6A-C9BE-AC96-227764A4A46A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
468+
{C882891F-4A6A-C9BE-AC96-227764A4A46A}.Debug|Any CPU.Build.0 = Debug|Any CPU
469+
{C882891F-4A6A-C9BE-AC96-227764A4A46A}.Release|Any CPU.ActiveCfg = Release|Any CPU
470+
{C882891F-4A6A-C9BE-AC96-227764A4A46A}.Release|Any CPU.Build.0 = Release|Any CPU
465471
EndGlobalSection
466472
GlobalSection(SolutionProperties) = preSolution
467473
HideSolutionNode = FALSE
@@ -542,6 +548,7 @@ Global
542548
{84823875-1B07-4CCE-A009-29AEF90C6C10} = {FF1089BE-C704-4374-B629-C57C08E1798F}
543549
{AA4EDA37-1D81-4235-A7F6-F1C112B364EF} = {FF1089BE-C704-4374-B629-C57C08E1798F}
544550
{8A46FEDF-FD76-48AB-9BB2-47254C7623A4} = {FF1089BE-C704-4374-B629-C57C08E1798F}
551+
{C882891F-4A6A-C9BE-AC96-227764A4A46A} = {FF1089BE-C704-4374-B629-C57C08E1798F}
545552
EndGlobalSection
546553
GlobalSection(ExtensibilityGlobals) = postSolution
547554
SolutionGuid = {D5EB1960-6F30-4CE1-B375-EAE1F787D6FF}
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
<Project Sdk="Microsoft.NET.Sdk.Razor">
2+
3+
<PropertyGroup>
4+
<Version>9.0.0-beta01</Version>
5+
</PropertyGroup>
6+
7+
<PropertyGroup>
8+
<PackageTags>Bootstrap Blazor WebAssembly wasm UI Components JuHe Ip Locator</PackageTags>
9+
<Description>Bootstrap UI components extensions of JuHeIpLocator</Description>
10+
</PropertyGroup>
11+
12+
<ItemGroup>
13+
<PackageReference Include="BootstrapBlazor" Version="9.3.1-beta24" />
14+
</ItemGroup>
15+
16+
</Project>
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
// Copyright (c) Argo Zhang (argo@163.com). All rights reserved.
2+
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
3+
// Website: https://www.blazor.zone or https://argozhang.github.io/
4+
5+
using BootstrapBlazor.Components;
6+
7+
namespace Microsoft.Extensions.DependencyInjection;
8+
9+
/// <summary>
10+
/// BootstrapBlazor 服务扩展类
11+
/// </summary>
12+
public static class BootstrapBlazoJuHeIpLocatorExtensions
13+
{
14+
/// <summary>
15+
/// 添加 AzureOpenAIService 服务
16+
/// </summary>
17+
/// <param name="services"></param>
18+
public static IServiceCollection AddBootstrapBlazorJuHeIpLocatorService(this IServiceCollection services)
19+
{
20+
services.AddSingleton<IIpLocatorProvider, JuHeIpLocatorProvider>();
21+
#if NET8_0_OR_GREATER
22+
services.AddKeyedSingleton<IIpLocatorProvider, JuHeIpLocatorProvider>("BootstrapBlazor.JuHeIpLocatorProvider");
23+
#endif
24+
25+
services.AddOptionsMonitor<JuHeIpLocatorOptions>();
26+
return services;
27+
}
28+
}
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
// Licensed to the .NET Foundation under one or more agreements.
2+
// The .NET Foundation licenses this file to you under the Apache 2.0 License
3+
// See the LICENSE file in the project root for more information.
4+
// Maintainer: Argo Zhang(argo@live.ca) Website: https://www.blazor.zone
5+
6+
namespace BootstrapBlazor.Components;
7+
8+
/// <summary>
9+
/// 聚合搜索引擎 IP 定位器配置类
10+
/// </summary>
11+
class JuHeIpLocatorOptions
12+
{
13+
/// <summary>
14+
/// 聚合搜索引擎 IP 定位器 AppKey
15+
/// </summary>
16+
public string Key { get; set; } = "";
17+
18+
/// <summary>
19+
/// 聚合搜索引擎 IP 定位器请求地址
20+
/// </summary>
21+
public string Url { get; set; } = "http://apis.juhe.cn/ip/ipNew";
22+
23+
/// <summary>
24+
/// 聚合搜索引擎 IP 定位器请求超时时间 默认 5 秒
25+
/// </summary>
26+
public TimeSpan Timeout { get; set; }
27+
}
Lines changed: 215 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,215 @@
1+
// Copyright (c) Argo Zhang (argo@163.com). All rights reserved.
2+
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
3+
// Website: https://www.blazor.zone or https://argozhang.github.io/
4+
5+
using Microsoft.Extensions.Configuration;
6+
using Microsoft.Extensions.Logging;
7+
using Microsoft.Extensions.Options;
8+
using System.Net.Http.Json;
9+
using System.Text.Json.Serialization;
10+
11+
namespace BootstrapBlazor.Components;
12+
13+
/// <summary>
14+
/// 聚合搜索引擎 IP 定位器
15+
/// </summary>
16+
/// <param name="httpClientFactory"></param>
17+
/// <param name="options"></param>
18+
/// <param name="juHeIpLocatorOptions"></param>
19+
/// <param name="logger"></param>
20+
class JuHeIpLocatorProvider(IHttpClientFactory httpClientFactory,
21+
IOptions<BootstrapBlazorOptions> options,
22+
IOptions<JuHeIpLocatorOptions> juHeIpLocatorOptions,
23+
ILogger<JuHeIpLocatorProvider> logger) : DefaultIpLocatorProvider(options)
24+
{
25+
private HttpClient? _client;
26+
27+
private JuHeIpLocatorOptions? _options;
28+
29+
/// <summary>
30+
/// <inheritdoc/>
31+
/// </summary>
32+
/// <param name="ip"></param>
33+
protected override async Task<string?> LocateByIp(string ip)
34+
{
35+
string? ret = null;
36+
_options ??= GetOptions();
37+
var url = $"{_options.Url}?ip={ip}&key={_options.Key}";
38+
try
39+
{
40+
_client ??= httpClientFactory.CreateClient();
41+
using var token = new CancellationTokenSource(_options.Timeout);
42+
ret = await Fetch(url, _client, token.Token);
43+
}
44+
catch (Exception ex)
45+
{
46+
logger.LogError(ex, "Url: {url}", url);
47+
}
48+
return ret;
49+
}
50+
51+
private JuHeIpLocatorOptions GetOptions()
52+
{
53+
var options = juHeIpLocatorOptions.Value;
54+
55+
try
56+
{
57+
if (string.IsNullOrEmpty(options.Key))
58+
{
59+
throw new InvalidOperationException($"{nameof(JuHeIpLocatorOptions)}:Key not value in appsettings configuration file. 未配置 {nameof(JuHeIpLocatorOptions)}:Key 请在 appsettings.json 中配置 {nameof(JuHeIpLocatorOptions)}:Key");
60+
}
61+
if (string.IsNullOrEmpty(options.Url))
62+
{
63+
options.Url = "http://apis.juhe.cn/ip/ipNewV3";
64+
}
65+
}
66+
catch (Exception ex)
67+
{
68+
logger.LogError(ex, "{GetOptions} failed", nameof(GetOptions));
69+
}
70+
return options;
71+
}
72+
73+
/// <summary>
74+
/// 请求获得地理位置接口方法
75+
/// </summary>
76+
/// <param name="url"></param>
77+
/// <param name="client"></param>
78+
/// <param name="token"></param>
79+
/// <returns></returns>
80+
protected virtual async Task<string?> Fetch(string url, HttpClient client, CancellationToken token)
81+
{
82+
var result = await client.GetFromJsonAsync<LocationResult>(url, token);
83+
if (result != null && result.ErrorCode != 0)
84+
{
85+
logger.LogError("ErrorCode: {ErrorCode} Reason: {Reason}", result.ErrorCode, result.Reason);
86+
}
87+
return result?.ToString();
88+
}
89+
90+
/// <summary>
91+
/// LocationResult 结构体
92+
/// </summary>
93+
class LocationResult
94+
{
95+
/// <summary>
96+
/// 获得/设置 结果状态返回码 为 查询成功 时通讯正常
97+
/// </summary>
98+
public string? Reason { get; set; }
99+
100+
/// <summary>
101+
/// 获得/设置 错误码
102+
/// </summary>
103+
[JsonPropertyName("error_code")]
104+
public int ErrorCode { get; set; }
105+
106+
/// <summary>
107+
/// 获得/设置 定位信息
108+
/// </summary>
109+
public LocationData? Result { get; set; }
110+
111+
/// <summary>
112+
/// <inheritdoc/>
113+
/// </summary>
114+
/// <returns></returns>
115+
public override string? ToString()
116+
{
117+
string? ret = null;
118+
if (ErrorCode == 0)
119+
{
120+
ret = Result?.Country == "中国"
121+
? $"{Result?.Prov}{Result?.City}{Result?.District} {Result?.Isp}"
122+
: $"{Result?.Continent} {Result?.Country} {Result?.City}";
123+
}
124+
return ret;
125+
}
126+
}
127+
128+
class LocationData
129+
{
130+
/// <summary>
131+
/// 获得/设置 州
132+
/// </summary>
133+
public string? Continent { get; set; }
134+
135+
/// <summary>
136+
/// 获得/设置 国家
137+
/// </summary>
138+
public string? Country { get; set; }
139+
140+
/// <summary>
141+
/// 获得/设置 邮编
142+
/// </summary>
143+
public string? ZipCode { get; set; }
144+
145+
/// <summary>
146+
/// 获得/设置 时区
147+
/// </summary>
148+
public string? TimeZone { get; set; }
149+
150+
/// <summary>
151+
/// 获得/设置 精度
152+
/// </summary>
153+
public string? Accuracy { get; set; }
154+
155+
/// <summary>
156+
/// 获得/设置 所属
157+
/// </summary>
158+
public string? Owner { get; set; }
159+
160+
/// <summary>
161+
/// 获得/设置 运营商
162+
/// </summary>
163+
public string? Isp { get; set; }
164+
165+
/// <summary>
166+
/// 获得/设置 来源
167+
/// </summary>
168+
public string? Source { get; set; }
169+
170+
/// <summary>
171+
/// 获得/设置 区号
172+
/// </summary>
173+
public string? AreaCode { get; set; }
174+
175+
/// <summary>
176+
/// 获得/设置 行政区划代码
177+
/// </summary>
178+
public string? AdCode { get; set; }
179+
180+
/// <summary>
181+
/// 获得/设置 国家代码
182+
/// </summary>
183+
public string? AsNumber { get; set; }
184+
185+
/// <summary>
186+
/// 获得/设置 经度
187+
/// </summary>
188+
public string? Lat { get; set; }
189+
190+
/// <summary>
191+
/// 获得/设置 纬度
192+
/// </summary>
193+
public string? Lng { get; set; }
194+
195+
/// <summary>
196+
/// 获得/设置 半径
197+
/// </summary>
198+
public string? Radius { get; set; }
199+
200+
/// <summary>
201+
/// 获得/设置 省份
202+
/// </summary>
203+
public string? Prov { get; set; }
204+
205+
/// <summary>
206+
/// 获得/设置 城市
207+
/// </summary>
208+
public string? City { get; set; }
209+
210+
/// <summary>
211+
/// 获得/设置 区县
212+
/// </summary>
213+
public string? District { get; set; }
214+
}
215+
}

0 commit comments

Comments
 (0)