From 5d5df1b5db8c77cf87e39d7a481e3bd8c90d7631 Mon Sep 17 00:00:00 2001 From: kennethcheng Date: Sun, 6 Sep 2026 20:39:14 +0800 Subject: [PATCH] =?UTF-8?q?=EF=BB=BFfix:=20GotifyApiService=20=E6=94=B9?= =?UTF-8?q?=E4=B8=BA=E7=BB=9D=E5=AF=B9=20URI=20+=20=E5=AE=9E=E6=97=B6?= =?UTF-8?q?=E9=87=8D=E8=AF=BB=20URL/Token?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 修复 bug:用户改完 Gotify URL 后,历史 API 仍然请求老地址(127.0.0.1:8080)。 根因:HttpClient.BaseAddress 在构造后只读,GotifyApiService 在构造函数里把 _config.Gotify.Url 写进 BaseAddress 后就不再更新,而用户通过主窗改 URL 只是改了 in-memory _config 对象,不会重建 HttpClient。 修法: - HttpClient 不再设 BaseAddress,所有请求走绝对 URI(BuildUri 每次从 _config 重新组装) - 加 SyncAuthHeaders 方法,URL 或 Token 变了就重置 DefaultRequestHeaders.Authorization - _config 字段类型从 GotifyConfig 改成 AppConfig,这样能读到完整引用,GetRecentMessagesAsync 之类的才会用新值 现在点「保存并重连」后,刷历史/WebSocket/通知 全部会用新 URL,不用重启 app。 验证:日志里看到从 127.0.0.1:8080 → kennethcheng.cc:50334,401 是 server 端 token 不认(下一步核对 Gotify 后台 Client Token)。 --- Services/GotifyApiService.cs | 49 ++++++++++++++++++++++++++++++------ 1 file changed, 42 insertions(+), 7 deletions(-) diff --git a/Services/GotifyApiService.cs b/Services/GotifyApiService.cs index e7b7f44..cf33c96 100644 --- a/Services/GotifyApiService.cs +++ b/Services/GotifyApiService.cs @@ -7,22 +7,55 @@ namespace WindowsGotify.Services; public sealed class GotifyApiService { - private readonly GotifyConfig _config; + private readonly AppConfig _config; private readonly Logger _logger; private readonly HttpClient _httpClient; + private readonly object _httpLock = new(); + private string _appliedBaseUrl = ""; + private string _appliedToken = ""; public GotifyApiService(AppConfig config, Logger logger) { - _config = config.Gotify; + _config = config; _logger = logger; _httpClient = new HttpClient { - BaseAddress = new Uri(_config.Url.TrimEnd('/') + "/"), Timeout = TimeSpan.FromSeconds(15) }; + SyncAuthHeaders(); + } - _httpClient.DefaultRequestHeaders.Authorization = - new AuthenticationHeaderValue("Bearer", _config.ClientToken); + /// + /// 检测 URL/Token 是否变了,变了就刷新 HttpClient 的 Authorization。 + /// 注意:HttpClient.BaseAddress 构造后只读,所以我们用绝对 URI 绕过。 + /// + private void SyncAuthHeaders() + { + var baseUrl = (_config.Gotify.Url ?? "").TrimEnd('/'); + var token = _config.Gotify.ClientToken ?? ""; + + if (_appliedBaseUrl == baseUrl && _appliedToken == token) + return; + + lock (_httpLock) + { + if (_appliedBaseUrl == baseUrl && _appliedToken == token) + return; + + // 显式给默认 header;实际请求走绝对 URI,所以 BaseAddress 永远为空 + _httpClient.DefaultRequestHeaders.Authorization = + string.IsNullOrEmpty(token) ? null : new AuthenticationHeaderValue("Bearer", token); + _appliedBaseUrl = baseUrl; + _appliedToken = token; + } + } + + private Uri BuildUri(string path) + { + var baseUrl = (_config.Gotify.Url ?? "").TrimEnd('/'); + if (string.IsNullOrEmpty(baseUrl)) + throw new InvalidOperationException("Gotify URL 未配置。请在主窗填入 URL 并保存。"); + return new Uri(baseUrl + "/" + path); } /// @@ -30,8 +63,9 @@ public sealed class GotifyApiService /// public async Task> GetMessagesSinceAsync(long sinceId, int limit, CancellationToken ct) { + SyncAuthHeaders(); var path = $"message?limit={Math.Clamp(limit, 1, 100)}&since={Math.Max(0, sinceId)}"; - using var response = await _httpClient.GetAsync(path, ct); + using var response = await _httpClient.GetAsync(BuildUri(path), ct); response.EnsureSuccessStatusCode(); await using var stream = await response.Content.ReadAsStreamAsync(ct); @@ -44,8 +78,9 @@ public sealed class GotifyApiService /// public async Task> GetRecentMessagesAsync(int limit, CancellationToken ct) { + SyncAuthHeaders(); var path = $"message?limit={Math.Clamp(limit, 1, 100)}&since=0"; - using var response = await _httpClient.GetAsync(path, ct); + using var response = await _httpClient.GetAsync(BuildUri(path), ct); response.EnsureSuccessStatusCode(); await using var stream = await response.Content.ReadAsStreamAsync(ct);