|
| 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