ASP.NET Core 3.1如何自定义HTTP 415状态码响应
问题成因
- 你的中间件逻辑执行时机太晚,且没有拦截响应写入。ASP.NET Core管道按中间件注册顺序执行,你调用
await next.Invoke()之后,后续的MVC/API格式化器、模型绑定组件已经检测到不支持的媒体类型,直接把默认的ProblemDetails格式415响应写入了响应流,这时候再调用WriteAsync就是在已有内容后面追加,自然会出现默认JSON和自定义文本拼接的结果。 - 调用
context.Response.Clear()无效是因为执行到这行代码时,响应已经开始向客户端发送(context.Response.HasStarted为true),此时响应头、部分响应体已经发到客户端,Clear方法只能清空服务器端还未发送的缓冲,没法撤回已经发送的内容,也无法覆盖已经写入的完整默认响应。
可行实现方案
方案1:纯中间件管道实现(符合优先修改请求管道的需求)
核心思路是在所有业务中间件执行前,用临时内存流替换原始响应流,拦截后续所有响应写入,等后续逻辑执行完再判断状态码,替换415的响应内容后再写回原始流。
注意:这个中间件必须注册在Configure方法里所有其他中间件的最前面,否则拦截不到后续中间件的响应写入。
public void Configure(IApplicationBuilder app) { // 自定义415响应中间件,放在管道最开头 app.Use(async (context, next) => { // 保存原始响应流引用 var originalResponseStream = context.Response.Body; // 创建临时内存流替换原始响应流,拦截后续写入 using var tempStream = new MemoryStream(); context.Response.Body = tempStream; await next(); // 检测到415状态码时替换响应内容 if (context.Response.StatusCode == StatusCodes.Status415UnsupportedMediaType) { // 清空临时流中已写入的默认响应 tempStream.SetLength(0); // 重置响应头,避免和默认的ProblemDetails头冲突 context.Response.ContentType = "text/plain; charset=utf-8"; context.Response.ContentLength = null; // 写入自定义内容 await context.Response.WriteAsync("Unsupported media type handled"); } // 将最终的响应内容拷贝回原始流,返回给客户端 tempStream.Seek(0, SeekOrigin.Begin); await tempStream.CopyToAsync(originalResponseStream); context.Response.Body = originalResponseStream; }); // 后续注册其他中间件,比如app.UseRouting()、app.UseEndpoints()等 // app.UseRouting(); // app.UseEndpoints(endpoints => endpoints.MapControllers()); }
方案2:配置API行为选项(更轻量的官方推荐方案)
如果不需要强制用纯中间件实现,可以直接在服务配置阶段替换API内置的415响应生成逻辑,不需要拦截所有请求的响应流,性能更好。
在Startup.cs的ConfigureServices方法中添加如下配置:
public void ConfigureServices(IServiceCollection services) { services.AddControllers() .ConfigureApiBehaviorOptions(options => { // 自定义415响应生成规则 options.UnsupportedMediaTypeTypeResolver = context => { // 可按需替换为JSON格式等其他自定义响应 return new ContentResult { StatusCode = StatusCodes.Status415UnsupportedMediaType, Content = "Unsupported media type handled", ContentType = "text/plain; charset=utf-8" }; }; }); }
注意事项
- 所有对响应状态码、响应头、响应体的修改,必须在
context.Response.HasStarted为false时执行,一旦响应开始发送,任何修改都不会生效。 - 中间件的注册顺序直接决定执行顺序,处理响应的逻辑如果要修改后续中间件生成的内容,必须提前注册并拦截响应流。
内容的提问来源于stack exchange,提问作者bytedev
相关产品推荐
相关产品推荐

