.NET中间件管道中图片HEAD请求的CORS问题及疑问
场景说明
- 服务器上存有若干TIF(F)文件,用户需要查看这些文件
- 通过工具将TIF文件转换为同名JPG文件,解决查看问题
- 前端发送HEAD请求,探测对应TIF文件的JPG版本是否存在
.NET中间件管道配置
app.UseCors(p => p .AllowAnyHeader() .AllowAnyMethod() .WithOrigins(builder.Configuration.GetAllowedOrigins()) .WithExposedHeaders("Content-Disposition")); app.UseStaticFiles(); var storageSettings = app.Services.GetRequiredService<IOptions<StorageSettings>>().Value; if (storageSettings.EnableDocumentProvider) { app.UseStaticFiles(new StaticFileOptions { ServeUnknownFileTypes = true, FileProvider = new PhysicalFileProvider(Path.Combine(AppDomain.CurrentDomain.BaseDirectory, storageSettings.AttachmentsPath)), RequestPath = $"/{AttachmentsManager.VirtualPath}" }); app.UseStaticFiles(new StaticFileOptions { ServeUnknownFileTypes = true, FileProvider = new PhysicalFileProvider(Path.Combine(AppDomain.CurrentDomain.BaseDirectory, storageSettings.DownloadsPath)), RequestPath = $"/{DownloadsManager.VirtualPath}" }); }
问题与尝试的解决方案
此前CORS功能正常,引入图片HEAD请求后,仅在禁用缓存时能正常运行。尝试过以下方案:
- 在
StaticFileOptions的OnPrepareResponse中添加Access-Control-Allow-Origin:*,问题解决但不够严谨:
app.UseStaticFiles(new StaticFileOptions { ServeUnknownFileTypes = true, FileProvider = new PhysicalFileProvider(Path.Combine(AppDomain.CurrentDomain.BaseDirectory, storageSettings.AttachmentsPath)), RequestPath = $"/{AttachmentsManager.VirtualPath}", OnPrepareResponse = ctx => { ctx.Context.Response.Headers.Append("Access-Control-Allow-Origin", "*"); // 新增代码 } });
- 实现自定义
StaticFilesCorsMiddleware,基于允许的来源添加响应头,但依赖Referer头稳定性不足:
public sealed class StaticFilesCorsMiddleware(IConfiguration configuration) : IMiddleware { private readonly string[] _allowedOrigins = configuration.GetAllowedOrigins(); public async Task InvokeAsync(HttpContext context, RequestDelegate next) { var currentOrigin = context.Request.Headers.Origin.ToString().TrimEnd('/'); var currentReferer = context.Request.Headers.Referer.ToString().TrimEnd('/'); var effectiveCaller = string.IsNullOrEmpty(currentOrigin) ? currentReferer : currentOrigin; if (_allowedOrigins.Contains(effectiveCaller) && !context.Response.Headers.AccessControlAllowOrigin.Any()) { context.Response.Headers.AccessControlAllowOrigin = effectiveCaller; } await next(context); } }
- 为
<img>标签添加crossOrigin="anonymous",彻底解决问题
核心疑问与解答
疑问
为何未引入HEAD请求时,无需crossOrigin属性即可正常运行?且仅HEAD请求出现CORS失败,<img>标签的GET请求无此问题。
解答
这是因为浏览器对不同资源请求的CORS规则存在差异:
<img>标签的GET请求属于简单请求中的嵌入资源请求,浏览器默认允许跨域加载这类资源,不需要服务器返回CORS响应头,也不会触发预检请求。即使没有crossOrigin属性,浏览器也能正常展示图片,但此时无法通过JavaScript读取图片内容(比如Canvas操作)。- 而用
fetch发送的HEAD请求属于非嵌入资源的跨域请求,这类请求不在浏览器的“豁免”范围内,必须符合CORS规则:服务器需要返回Access-Control-Allow-Origin等响应头,否则浏览器会拦截响应,抛出CORS错误。
关于缓存导致的问题:
当GET请求被缓存后,后续的HEAD请求如果命中缓存,但缓存的响应中没有包含CORS头,浏览器会因为HEAD请求需要CORS验证而判定失败。添加crossOrigin="anonymous"后,浏览器会强制为<img>的GET请求带上Origin头,并要求服务器返回CORS响应头,此时缓存的响应会包含正确的CORS头,后续HEAD请求验证时就能通过。
内容的提问来源于stack exchange,提问作者Renat Zamaletdinov
相关产品推荐
相关产品推荐

