You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

ASP.NET Core REST API如何在创建资源前提前返回406 Not Acceptable?

问题原因

默认ASP.NET Core的内容协商逻辑在结果执行阶段才会触发,也就是Action方法执行完毕、准备生成响应报文时才会校验Accept头是否匹配支持的媒体类型,这就会导致非幂等的POST请求先执行业务逻辑写库,之后才返回406的问题。

解决方案

通过自定义操作过滤器,将Accept头校验逻辑提前到Action方法执行前触发,匹配失败直接返回406,不会进入业务逻辑。

步骤1:实现校验过滤器

using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.Controllers;
using Microsoft.AspNetCore.Mvc.Filters;
using Microsoft.Net.Http.Headers;

public class AcceptHeaderValidationFilter : IActionFilter
{
    public void OnActionExecuting(ActionExecutingContext context)
    {
        // 仅校验控制器/Action标注了[Produces]的接口
        if (context.ActionDescriptor is not ControllerActionDescriptor controllerActionDescriptor) 
            return;

        var producesAttributes = controllerActionDescriptor.MethodInfo.GetCustomAttributes(true)
            .Concat(controllerActionDescriptor.ControllerTypeInfo.GetCustomAttributes(true))
            .OfType<ProducesAttribute>()
            .ToList();
        
        if (!producesAttributes.Any()) 
            return;

        // 收集接口声明支持的所有响应媒体类型
        var supportedMediaTypes = producesAttributes
            .SelectMany(p => p.ContentTypes)
            .Select(ct => ct.ToString())
            .ToHashSet(StringComparer.OrdinalIgnoreCase);

        // 无Accept头时遵循HTTP规范默认放行
        var acceptHeaders = context.HttpContext.Request.GetTypedHeaders().Accept;
        if (acceptHeaders == null || !acceptHeaders.Any()) 
            return;

        // 校验是否存在匹配的、权重大于0的媒体类型
        var hasValidMatch = acceptHeaders
            .Where(h => h.Quality == null || h.Quality > 0)
            .Any(h => 
                supportedMediaTypes.Contains(h.MediaType.Value) 
                // 不需要支持通配符Accept可以删掉下行逻辑
                || h.MediaType.Value == "*/*"
            );

        if (!hasValidMatch)
        {
            context.Result = new StatusCodeResult(StatusCodes.Status406NotAcceptable);
        }
    }

    public void OnActionExecuted(ActionExecutedContext context)
    {
        // 无需实现
    }
}

步骤2:注册全局过滤器

在服务注册时把过滤器添加到MVC配置中即可全局生效:

services.AddControllers(configure => 
{
    configure.ReturnHttpNotAcceptable = true;
    // 注册Accept头校验过滤器
    configure.Filters.Add<AcceptHeaderValidationFilter>();
})
.AddNewtonsoftJson(setup =>
{
    setup.SerializerSettings.ContractResolver = new CamelCasePropertyNamesContractResolver();
})
.AddXmlDataContractSerializerFormatters();

注意事项

  • 过滤器只会对标注了[Produces]特性的控制器或Action生效,无该特性的接口不会触发校验
  • 默认支持*/*通配符Accept头,不需要严格匹配的话可以保留该逻辑,需要严格校验仅支持指定媒体类型的话删除对应判断即可
  • 校验逻辑自动兼容Accept头的质量值(q参数),权重为0的媒体类型会被直接判定为不匹配

内容的提问来源于stack exchange,提问作者Daniel Medrano

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.09.27 21:36:01