如何为指定控制器及Action方法注册自定义InputFormatter
实现自定义InputFormatter仅应用于特定控制器/Action
有两种常用方案可以实现这个需求,以下是具体做法:
方案一:通过自定义特性控制生效范围
这是最灵活的方式,通过标记特性来指定哪些控制器或Action可以使用该InputFormatter:
1. 创建自定义标记特性
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method)] public class UseCspReportInputFormatterAttribute : Attribute { }
2. 修改自定义InputFormatter,添加生效判断逻辑
重写CanRead方法,仅当当前请求的控制器或Action标记了上述特性时,才允许处理请求:
public class CSPReportInputFormatter : TextInputFormatter { public CSPReportInputFormatter() { SupportedMediaTypes.Add(MediaTypeHeaderValue.Parse("application/csp-report")); SupportedEncodings.Add(Encoding.UTF8); } // 重写CanRead方法,限制生效范围 public override bool CanRead(InputFormatterContext context) { // 先检查基础条件(媒体类型、编码是否匹配) if (!base.CanRead(context)) return false; // 检查当前端点是否存在自定义特性 var endpoint = context.HttpContext.GetEndpoint(); return endpoint?.Metadata.GetMetadata<UseCspReportInputFormatterAttribute>() != null; } public override async Task<InputFormatterResult> ReadRequestBodyAsync(InputFormatterContext context, Encoding encoding) { if (context == null) { throw new ArgumentNullException(nameof(context)); } using var reader = new StreamReader(context.HttpContext.Request.Body); string responseString = await reader.ReadToEndAsync(); var data = JsonConvert.DeserializeObject<ContentSecurityPolicyRequest>(responseString); return await InputFormatterResult.SuccessAsync(data); } }
3. 在目标控制器/Action上标记特性
全局注册InputFormatter的代码保持不变,然后在需要的控制器或Action上添加特性:
// 给整个控制器标记 [UseCspReportInputFormatter] [ApiController] [Route("api/csp")] public class CspReportController : ControllerBase { [HttpPost("report")] public IActionResult ReceiveReport(ContentSecurityPolicyRequest report) { // 处理CSP上报逻辑 return Ok(); } } // 或者仅给单个Action标记 [ApiController] [Route("api/other")] public class OtherController : ControllerBase { [HttpPost("csp-report")] [UseCspReportInputFormatter] public IActionResult HandleCspReport(ContentSecurityPolicyRequest report) { // 处理逻辑 return Ok(); } }
方案二:移除全局注册,仅在特定控制器配置
如果不想全局注册该InputFormatter,可以通过控制器的依赖注入获取MvcOptions并添加,但这种方式灵活性较低:
1. 移除全局注册的代码
修改ConfigureServices,不再全局添加InputFormatter:
public void ConfigureServices(IServiceCollection services) { services.AddControllers(); }
2. 在目标控制器中添加InputFormatter
通过构造注入获取MvcOptions,添加自定义InputFormatter:
[ApiController] [Route("api/csp")] public class CspReportController : ControllerBase { public CspReportController(MvcOptions mvcOptions) { // 检查是否已添加,避免重复注册 if (!mvcOptions.InputFormatters.Any(f => f is CSPReportInputFormatter)) { mvcOptions.InputFormatters.Insert(0, new CSPReportInputFormatter()); } } [HttpPost("report")] public IActionResult ReceiveReport(ContentSecurityPolicyRequest report) { return Ok(); } }
注意:方案二会让该InputFormatter对所有匹配媒体类型的请求生效,但只有目标控制器的Action能接收解析后的模型,其他控制器即使收到
application/csp-report类型的请求,也无法完成模型绑定。相比之下,方案一的控制精度更高。
内容的提问来源于stack exchange,提问作者Baibhav Shukla
相关产品推荐
相关产品推荐

