ASP.NET Core Minimal API中如何为415错误返回JSON响应体
解决ASP.NET Core Minimal API无Content-Type头返回415无响应体问题
问题根源
当请求未携带Content-Type头时,ASP.NET Core会在模型绑定阶段直接返回415(Unsupported Media Type)状态码,这个过程没有抛出异常,而是由框架内部直接生成了空响应体。由于UseExceptionHandler仅处理未捕获的异常,因此无法拦截这个场景。
解决方案
可以通过以下几种方式自定义415错误的响应内容:
方法1:使用UseStatusCodePages全局处理状态码
这个方法可以捕获所有返回415状态码的请求,统一返回自定义JSON响应:
修改Program.cs的中间件顺序,在UseExceptionHandler之前添加UseStatusCodePages:
using System.Runtime.CompilerServices; using Microsoft.AspNetCore.Mvc; [assembly: InternalsVisibleTo("TestProject1")] var builder = WebApplication.CreateBuilder(args); builder.Services.Configure<RouteHandlerOptions>(o => o.ThrowOnBadRequest = true); builder.Services.AddProblemDetails(); // 可选,用于生成符合RFC规范的问题详情 var app = builder.Build(); // 处理415等状态码的自定义响应 app.UseStatusCodePages(async context => { var response = context.HttpContext.Response; if (response.StatusCode == StatusCodes.Status415UnsupportedMediaType) { response.ContentType = "application/json"; await response.WriteAsJsonAsync(new { type = "https://tools.ietf.org/html/rfc7231#section-6.5.13", title = "Unsupported Media Type", status = 415, detail = "请求缺少Content-Type头,该端点需要携带application/json类型的请求体。", instance = context.HttpContext.Request.Path }); } }); app.UseExceptionHandler(handlerApp => handlerApp.Run(async context => { context.Response.StatusCode = 400; context.Response.ContentType = "application/json"; await context.Response.WriteAsJsonAsync(new { error = "服务器错误", message = "发生了未预期的异常。" }); })); app.MapPost("/hi", ([FromBody] Request request) => "Hello World!"); app.MapPost("/throwerror", ([FromBody] Request request) => throw new Exception("Ouch")); app.Run(); record Request(int Id);
方法2:自定义中间件精准拦截无Content-Type的请求
如果只想针对带请求体的方法(如POST/PUT)拦截无Content-Type的情况,可以添加自定义中间件:
app.Use(async (context, next) => { // 仅针对POST/PUT等包含请求体的方法检查 if ((context.Request.Method == HttpMethods.Post || context.Request.Method == HttpMethods.Put) && string.IsNullOrEmpty(context.Request.ContentType)) { context.Response.StatusCode = StatusCodes.Status415UnsupportedMediaType; context.Response.ContentType = "application/json"; await context.Response.WriteAsJsonAsync(new { error = "Unsupported Media Type", message = "必须提供Content-Type头,建议使用application/json。" }); return; } await next(); }); // 后续中间件... app.UseExceptionHandler(...); app.MapPost(...);
方法3:端点过滤器针对特定端点处理
如果只需要给部分端点添加检查,可以使用端点过滤器:
var contentTypeFilter = new EndpointFilterDelegate(async (context, next) => { if (string.IsNullOrEmpty(context.HttpContext.Request.ContentType)) { context.HttpContext.Response.StatusCode = StatusCodes.Status415UnsupportedMediaType; context.HttpContext.Response.ContentType = "application/json"; await context.HttpContext.Response.WriteAsJsonAsync(new { error = "缺少Content-Type头", message = "该端点需要携带Content-Type头。" }); return Results.StatusCode(415); } return await next(context); }); app.MapPost("/hi", ([FromBody] Request request) => "Hello World!") .AddEndpointFilter(contentTypeFilter);
修改测试用例
针对第一个测试,调整断言以匹配自定义的JSON响应:
[TestMethod] public async Task TestHiRespondsWithJsonErrorOnNoContentType() { var factory = new WebApplicationFactory<Program>(); var client = factory.CreateClient(); var response = await client.PostAsync("/hi", new StringContent("{ \"Id\": 123 }", Encoding.UTF8)); Assert.AreEqual(HttpStatusCode.UnsupportedMediaType, response.StatusCode); Assert.AreEqual("application/json", response.Content.Headers.ContentType?.MediaType); var error = await response.Content.ReadFromJsonAsync<dynamic>(); Assert.AreEqual("Unsupported Media Type", error.title); Assert.AreEqual(415, error.status); }
内容的提问来源于stack exchange,提问作者Christian Findlay
相关产品推荐
相关产品推荐

