feat: fetch 请求增加 If-Modified-Since

This commit is contained in:
uye
2025-07-08 21:54:43 +08:00
parent 9658323221
commit ca7c779e10
3 changed files with 81 additions and 51 deletions

View File

@@ -28,15 +28,19 @@ namespace MaaWpfGui.Extensions
var uri = response?.RequestMessage?.RequestUri;
var statusCode = response?.StatusCode.ToString();
var etag = response?.Headers.ETag?.Tag;
var lastModified = response?.Content?.Headers?.LastModified?.ToString("R"); // RFC1123
if (response != null && (response.IsSuccessStatusCode || response.StatusCode == HttpStatusCode.NotModified))
{
_logger.Information("HTTP: {StatusCode} {Method} {Url} {Etag}", statusCode, method, uri?.GetLeftPart(uriPartial), etag);
_logger.Information("HTTP: {StatusCode} {Method} {Url} {ETag} {LastModified}",
statusCode, method, uri?.GetLeftPart(uriPartial), etag, lastModified);
}
else
{
_logger.Warning("HTTP: {StatusCode} {Method} {Url} {Etag}", statusCode, method, uri?.GetLeftPart(uriPartial), etag);
_logger.Warning("HTTP: {StatusCode} {Method} {Url} {ETag} {LastModified}",
statusCode, method, uri?.GetLeftPart(uriPartial), etag, lastModified);
}
}
}
}

View File

@@ -27,76 +27,115 @@ namespace MaaWpfGui.Helper
{
private static readonly ILogger _logger = Log.ForContext<ETagCache>();
private static readonly string _cacheFile = Path.Combine(Environment.CurrentDirectory, "cache/etag.json");
private static Dictionary<string, string> _cache = [];
private static readonly string _etagFile = Path.Combine(Environment.CurrentDirectory, "cache/etag.json");
private static readonly string _lastModifiedFile = Path.Combine(Environment.CurrentDirectory, "cache/last_modified.json");
private static Dictionary<string, string> _etagCache = [];
private static Dictionary<string, DateTimeOffset> _lastModifiedCache = [];
public static void Load()
{
if (File.Exists(_cacheFile) is false)
// ETag
if (File.Exists(_etagFile))
{
_cache = [];
return;
try
{
var json = File.ReadAllText(_etagFile);
_etagCache = JsonConvert.DeserializeObject<Dictionary<string, string>>(json) ?? [];
}
catch (Exception e)
{
_logger.Warning(e, "Failed to load ETag cache");
_etagCache = [];
}
}
try
// Last-Modified
if (File.Exists(_lastModifiedFile))
{
var jsonStr = File.ReadAllText(_cacheFile);
_cache = JsonConvert.DeserializeObject<Dictionary<string, string>>(jsonStr) ?? [];
}
catch (Exception e)
{
_logger.Error(e.Message);
try
{
var json = File.ReadAllText(_lastModifiedFile);
_lastModifiedCache = JsonConvert.DeserializeObject<Dictionary<string, DateTimeOffset>>(json) ?? [];
}
catch (Exception e)
{
_logger.Warning(e, "Failed to load Last-Modified cache");
_lastModifiedCache = [];
}
}
}
public static void Save()
{
var jsonStr = JsonConvert.SerializeObject(_cache);
File.WriteAllText(_cacheFile, jsonStr);
File.WriteAllText(_etagFile, JsonConvert.SerializeObject(_etagCache, Formatting.Indented));
File.WriteAllText(_lastModifiedFile, JsonConvert.SerializeObject(_lastModifiedCache, Formatting.Indented));
}
// ReSharper disable once MemberCanBePrivate.Global
public static string Get(string? url)
public static string GetETag(string? url) =>
url != null && _etagCache.TryGetValue(url, out var etag) ? etag : string.Empty;
public static DateTimeOffset? GetLastModified(string? url) =>
url != null && _lastModifiedCache.TryGetValue(url, out var lm) ? lm : null;
public static void SetETag(string url, string etag)
{
if (url is null)
_etagCache[url] = etag;
}
public static void SetLastModified(string url, DateTimeOffset? dt)
{
if (dt.HasValue)
{
return string.Empty;
_lastModifiedCache[url] = dt.Value;
}
return _cache.TryGetValue(url, out string? ret) ? ret : string.Empty;
}
// ReSharper disable once MemberCanBePrivate.Global
public static void Set(string url, string etag)
{
_cache[url] = etag;
Save();
}
// UPDATE: 重定向会导致 uri 变成其他地址,导致存的 ETag 无法匹配原始地址,所以要传入原始地址
public static void Set(HttpResponseMessage? response, string uri)
{
var etag = response?.Headers.ETag?.Tag;
if (string.IsNullOrEmpty(uri) || string.IsNullOrEmpty(etag))
if (response == null || string.IsNullOrEmpty(uri))
{
return;
}
Set(uri, etag);
var etag = response.Headers.ETag?.Tag;
var lastModified = response.Content?.Headers?.LastModified;
if (!string.IsNullOrEmpty(etag))
{
SetETag(uri, etag);
}
if (lastModified.HasValue)
{
SetLastModified(uri, lastModified.Value);
}
Save();
}
public static async Task<HttpResponseMessage?> FetchResponseWithEtag(string url, bool force = false)
{
var etag = force ? string.Empty : Get(url);
Dictionary<string, string> headers = new Dictionary<string, string>
var headers = new Dictionary<string, string>
{
{ "Accept", "application/octet-stream" },
{ "Connection", "close" },
};
if (!string.IsNullOrEmpty(etag))
if (!force)
{
headers["If-None-Match"] = etag;
var etag = GetETag(url);
var lastModified = GetLastModified(url);
if (!string.IsNullOrEmpty(etag))
{
headers["If-None-Match"] = etag;
}
if (lastModified.HasValue)
{
headers["If-Modified-Since"] = lastModified.Value.ToUniversalTime().ToString("R");
}
}
try

View File

@@ -65,7 +65,8 @@ namespace MaaWpfGui.Services
// 清理旧的缓存文件
const string CacheAllFileDownloadComplete = "cache/allFileDownloadComplete.json";
const string LastUpdateTime = "cache/LastUpdateTime.json";
var filesToClean = new[] { CacheAllFileDownloadComplete, LastUpdateTime };
const string StageAndTasksUpdateTime = "cache/stageAndTasksUpdateTime.json";
var filesToClean = new[] { CacheAllFileDownloadComplete, LastUpdateTime, StageAndTasksUpdateTime };
foreach (var file in filesToClean)
{
@@ -82,10 +83,7 @@ namespace MaaWpfGui.Services
}
}
const string StageAndTasksUpdateTime = "cache/stageAndTasksUpdateTime.json";
MergePermanentAndActivityStages(await LoadWebStages());
var unixTimestamp = (long)(DateTime.UtcNow - new DateTime(1970, 1, 1)).TotalSeconds;
await File.WriteAllTextAsync(StageAndTasksUpdateTime, GenerateJsonString("timestamp", new(unixTimestamp)));
_ = Execute.OnUIThreadAsync(() =>
{
@@ -98,17 +96,6 @@ namespace MaaWpfGui.Services
};
Growl.Info(growlInfo);
});
return;
static string GenerateJsonString(string key, JValue value)
{
JObject json = new JObject
{
[key] = value,
};
return JsonConvert.SerializeObject(json);
}
}
private static string GetClientType()