ASP.NET Core自定义CsvOutputFormatter无法匹配带参数的Accept头问题求助
这个问题的核心在于ASP.NET Core的内容协商默认会严格匹配媒体类型的完整字符串,包括后面的参数(比如charset、v=1.0),而你的代码里有两个关键限制导致带参数的请求无法匹配:
CanWriteResult方法里直接判断context.ContentType != "text/csv",硬编码的字符串相等校验会直接排除所有带参数的情况;- 试图通过添加所有可能的带参数媒体类型到
SupportedMediaTypes,但这根本不可能覆盖所有用户可能发送的参数组合(比如不同的charset、版本号)。
下面是具体的修复步骤:
第一步:修复CanWriteResult的媒体类型判断逻辑
不要直接比较字符串,而是用MediaTypeHeaderValue来解析请求的媒体类型,只校验主类型和子类型(也就是text/csv),忽略后面的参数。如果需要处理版本参数,也可以在这里解析判断:
public override bool CanWriteResult(OutputFormatterCanWriteContext context) { // 先尝试解析请求的Content-Type if (!MediaTypeHeaderValue.TryParse(context.ContentType, out var requestMediaType)) return false; // 只匹配主类型+子类型,忽略所有参数(charset、version等) if (!requestMediaType.MediaType.Equals("text/csv", StringComparison.OrdinalIgnoreCase)) return false; // 可选:如果需要校验版本参数,在这里处理 var versionParam = requestMediaType.Parameters.FirstOrDefault(p => p.Name.Equals("v", StringComparison.OrdinalIgnoreCase)); if (versionParam != null) { // 比如只允许v1.0和v2.0 var allowedVersions = new[] { "1.0", "2.0" }; if (!allowedVersions.Contains(versionParam.Value, StringComparer.OrdinalIgnoreCase)) return false; } // 原来的业务逻辑校验 var resource = context.Object as Resource; return resource != null && resource.Embedded != null && resource.Embedded.ContainsKey(Common.Constants.ListItems); }
第二步:精简SupportedMediaTypes的配置
只需要添加基础的text/csv即可,不需要带任何参数——TextOutputFormatter会自动处理编码相关的参数,而我们已经在CanWriteResult里处理了版本参数:
public CsvOutputFormatter() { // 只添加基础媒体类型,不用带参数 SupportedMediaTypes.Add(MediaTypeHeaderValue.Parse("text/csv")); // 编码配置保持不变 foreach (var encodingInfo in Encoding.GetEncodings()) { var encoding = encodingInfo.GetEncoding(); if (encoding == Encoding.UTF8) SupportedEncodings.Insert(0, encoding); else SupportedEncodings.Add(encoding); } }
第三步:简化控制器的[Produces]属性
只需要保留text/csv即可,不需要带任何参数,因为参数是请求时的动态内容:
[Produces("application/hal+json", "text/csv")] public class YourController : ControllerBase { // ... 你的控制器方法 }
为什么之前的方案无效?
- 把格式化器放首位:会导致所有请求都优先用CSV,破坏了默认返回JSON的需求;
- 添加带参数的
SupportedMediaTypes:内容协商会精确匹配完整的媒体类型字符串,而用户可能发送的参数组合是无限的(比如不同charset、版本号的组合),根本不可能穷举; [Produces]加所有参数组合:同样不现实,编译时无法预知所有可能的参数。
验证效果
修改后,当用户发送text/csv; charset=utf-8、text/csv; v=2.0或者混合参数的请求时,ASP.NET Core会正确识别这属于text/csv媒体类型,然后进入你的CanWriteResult方法,通过校验后就会使用CSV格式化器。
从日志来看,之前的问题是系统认为带参数的text/csv; charset=utf-8不是显式设置的text/csv的子集——修改判断逻辑后,这个问题就解决了。
内容的提问来源于stack exchange,提问作者wertzui
相关产品推荐
相关产品推荐

