ASP.NET Core 5控制器中如何拦截POST请求中的无效JSON参数
检测ASP.NET Core 5 API中的未知POST参数并返回400错误
针对你的需求,有两种比较优雅的方案,比你之前尝试的ActionFilter或用object接收的方式更贴合ASP.NET Core的设计思路:
方案一:利用System.Text.Json内置配置+全局异常过滤器
ASP.NET Core 5默认用System.Text.Json做序列化,它的默认行为是忽略请求中的未知字段,不会抛出异常。我们可以修改这个规则,让它遇到未定义字段时抛出异常,再通过全局异常过滤器捕获并返回400错误。
步骤1:修改Json序列化配置
在Startup.cs的ConfigureServices方法里,配置控制器的Json选项:
services.AddControllers() .AddJsonOptions(options => { // 关闭"忽略未知字段",遇到未定义属性时抛出JsonException options.JsonSerializerOptions.IgnoreUnknownFields = false; });
步骤2:创建全局异常过滤器
写一个过滤器专门捕获JsonException,把它转换成标准的400错误响应:
public class JsonUnknownFieldExceptionFilter : IExceptionFilter { private readonly ILogger<JsonUnknownFieldExceptionFilter> _logger; public JsonUnknownFieldExceptionFilter(ILogger<JsonUnknownFieldExceptionFilter> logger) { _logger = logger; } public void OnException(ExceptionContext context) { if (context.Exception is JsonException jsonEx) { _logger.LogError(jsonEx, "请求包含未定义的参数"); context.Result = new BadRequestObjectResult(new { StatusCode = StatusCodes.Status400BadRequest, Message = "请求包含无效参数", Detail = jsonEx.Message }); context.ExceptionHandled = true; } } }
步骤3:注册全局过滤器
同样在Startup.cs的ConfigureServices中,把过滤器添加到控制器配置里:
services.AddControllers(options => { options.Filters.Add<JsonUnknownFieldExceptionFilter>(); }) .AddJsonOptions(options => { options.JsonSerializerOptions.IgnoreUnknownFields = false; });
这样一来,只要请求里有未定义的参数,System.Text.Json会直接抛出异常,过滤器捕获后立刻返回400错误,请求根本不会到达你的控制器方法,完全符合你的需求。
方案二:使用Json Schema验证(精细化控制方案)
如果需要针对不同接口做差异化处理(比如部分接口允许未知字段,部分不允许),可以用Json Schema做请求体验证。这种方式需要引入System.Text.Json.Schema NuGet包(如果用Newtonsoft.Json替代默认序列化,也可以用它对应的Schema包)。
示例代码(System.Text.Json.Schema版本)
- 先安装NuGet包:
System.Text.Json.Schema - 创建一个ActionFilter实现验证逻辑:
public class ValidateRequestSchemaAttribute : ActionFilterAttribute { private readonly JsonSchema _schema; public ValidateRequestSchemaAttribute(Type requestType) { // 从请求模型类型自动生成Json Schema _schema = JsonSchemaGenerator.Generate(requestType); } public override async Task OnActionExecutionAsync(ActionExecutingContext context, ActionExecutionDelegate next) { var request = context.HttpContext.Request; request.Body.Position = 0; // 重置请求体读取位置,确保能完整读取 var requestBody = await new StreamReader(request.Body).ReadToEndAsync(); var validationResult = JsonSchemaValidator.Validate(requestBody, _schema); if (!validationResult.IsValid) { context.Result = new BadRequestObjectResult(new { StatusCode = StatusCodes.Status400BadRequest, Message = "请求参数不符合要求", Errors = validationResult.Errors.Select(e => e.Message) }); return; } await next(); } }
- 在需要验证的控制器方法上标记这个特性:
[HttpPost] [ValidateRequestSchema(typeof(MyRequestType))] public async Task<MyResponseType> Post([FromBody] MyRequestType request) { // 业务处理代码... }
这种方案的优势是灵活性高,可以针对单个接口配置验证规则,但代码量会比方案一多一些。
为什么不推荐你之前的尝试?
- ActionFilter手动解析请求体:需要重复读取请求体(默认请求体只能读一次,还要手动重置位置),不仅繁琐,还可能影响性能。
- 用
object接收请求:需要手动反射检查属性,代码不够优雅,也没法利用ASP.NET Core自带的模型绑定和验证机制。
上面两种方案都能很好解决你的问题,方案一更简洁适合全局统一规则,方案二更灵活适合精细化场景,你可以根据自己的需求选择。
内容的提问来源于stack exchange,提问作者the-robot




