fix: GotifyApiService 改为绝对 URI + 实时重读 URL/Token
修复 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)。
This commit is contained in:
@@ -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();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 检测 URL/Token 是否变了,变了就刷新 HttpClient 的 Authorization。
|
||||
/// 注意:HttpClient.BaseAddress 构造后只读,所以我们用绝对 URI 绕过。
|
||||
/// </summary>
|
||||
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 =
|
||||
new AuthenticationHeaderValue("Bearer", _config.ClientToken);
|
||||
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);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -30,8 +63,9 @@ public sealed class GotifyApiService
|
||||
/// </summary>
|
||||
public async Task<List<GotifyMessage>> 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
|
||||
/// </summary>
|
||||
public async Task<List<GotifyMessage>> 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);
|
||||
|
||||
Reference in New Issue
Block a user