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
|
public sealed class GotifyApiService
|
||||||
{
|
{
|
||||||
private readonly GotifyConfig _config;
|
private readonly AppConfig _config;
|
||||||
private readonly Logger _logger;
|
private readonly Logger _logger;
|
||||||
private readonly HttpClient _httpClient;
|
private readonly HttpClient _httpClient;
|
||||||
|
private readonly object _httpLock = new();
|
||||||
|
private string _appliedBaseUrl = "";
|
||||||
|
private string _appliedToken = "";
|
||||||
|
|
||||||
public GotifyApiService(AppConfig config, Logger logger)
|
public GotifyApiService(AppConfig config, Logger logger)
|
||||||
{
|
{
|
||||||
_config = config.Gotify;
|
_config = config;
|
||||||
_logger = logger;
|
_logger = logger;
|
||||||
_httpClient = new HttpClient
|
_httpClient = new HttpClient
|
||||||
{
|
{
|
||||||
BaseAddress = new Uri(_config.Url.TrimEnd('/') + "/"),
|
|
||||||
Timeout = TimeSpan.FromSeconds(15)
|
Timeout = TimeSpan.FromSeconds(15)
|
||||||
};
|
};
|
||||||
|
SyncAuthHeaders();
|
||||||
|
}
|
||||||
|
|
||||||
_httpClient.DefaultRequestHeaders.Authorization =
|
/// <summary>
|
||||||
new AuthenticationHeaderValue("Bearer", _config.ClientToken);
|
/// 检测 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 =
|
||||||
|
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>
|
/// <summary>
|
||||||
@@ -30,8 +63,9 @@ public sealed class GotifyApiService
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public async Task<List<GotifyMessage>> GetMessagesSinceAsync(long sinceId, int limit, CancellationToken ct)
|
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)}";
|
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();
|
response.EnsureSuccessStatusCode();
|
||||||
|
|
||||||
await using var stream = await response.Content.ReadAsStreamAsync(ct);
|
await using var stream = await response.Content.ReadAsStreamAsync(ct);
|
||||||
@@ -44,8 +78,9 @@ public sealed class GotifyApiService
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public async Task<List<GotifyMessage>> GetRecentMessagesAsync(int limit, CancellationToken ct)
|
public async Task<List<GotifyMessage>> GetRecentMessagesAsync(int limit, CancellationToken ct)
|
||||||
{
|
{
|
||||||
|
SyncAuthHeaders();
|
||||||
var path = $"message?limit={Math.Clamp(limit, 1, 100)}&since=0";
|
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();
|
response.EnsureSuccessStatusCode();
|
||||||
|
|
||||||
await using var stream = await response.Content.ReadAsStreamAsync(ct);
|
await using var stream = await response.Content.ReadAsStreamAsync(ct);
|
||||||
|
|||||||
Reference in New Issue
Block a user