.NET Core WEB API Modelbinding:如何使用JSON Schema实现验证
实现方案总览
ASP.NET Core没有内置JSON Schema校验的原生中间件,你可以通过两种路径实现需求:
- 全局中间件:对所有PUT/POST/PATCH请求统一校验,适合校验规则完全统一的场景
- 动作过滤器/端点过滤器:可以按接口指定不同的JSON Schema,灵活度更高,是大部分场景的首选方案
前置准备
先根据你项目使用的序列化器安装对应校验库:
- 若使用
System.Text.Json作为默认序列化器:安装JsonSchema.NetNuGet包 - 若使用Newtonsoft.Json作为默认序列化器:安装
Newtonsoft.Json.SchemaNuGet包
你可以将JSON Schema文件的属性设置为嵌入式资源,或者放到配置目录下,通过配置项读取路径,符合你希望在Startup中通过配置选项注册的需求:
- 在
appsettings.json中添加Schema路径配置:
"JsonSchemaSettings": { "CreateUserSchema": "YourProjectName.Schemas.CreateUserSchema.json", "UpdateOrderSchema": "YourProjectName.Schemas.UpdateOrderSchema.json" }
- 定义配置类并在Startup中注册:
// 配置类 public class JsonSchemaSettings { public string CreateUserSchema { get; set; } public string UpdateOrderSchema { get; set; } } // Startup.cs ConfigureServices方法中注册 services.Configure<JsonSchemaSettings>(Configuration.GetSection("JsonSchemaSettings"));
推荐实现:动作过滤器(全版本.NET Core适用)
该方案支持按接口单独指定Schema,不需要全局生效时不用注册全局过滤器,仅在需要校验的接口上加Attribute即可:
public class JsonSchemaValidationAttribute : ActionFilterAttribute { private readonly JsonSchema _schema; // 传入对应配置项的Key即可加载对应Schema public JsonSchemaValidationAttribute(string schemaConfigKey) { // 读取配置 var config = new ConfigurationBuilder() .AddJsonFile("appsettings.json") .Build(); var schemaPath = config.GetValue<string>($"JsonSchemaSettings:{schemaConfigKey}"); // 读取嵌入式Schema文件,以下为Newtonsoft.Json.Schema示例,System.Text.Json逻辑类似 using var stream = Assembly.GetExecutingAssembly().GetManifestResourceStream(schemaPath); using var reader = new StreamReader(stream); _schema = JsonSchema.Parse(reader.ReadToEnd()); } public override void OnActionExecuting(ActionExecutingContext context) { // 仅校验PUT/POST/PATCH请求 var method = context.HttpContext.Request.Method; if (method is not HttpMethods.Post and not HttpMethods.Put and not HttpMethods.Patch) { base.OnActionExecuting(context); return; } // 开启请求体缓冲,避免读完之后后续中间件/控制器读不到请求体 context.HttpContext.Request.EnableBuffering(); using var reader = new StreamReader(context.HttpContext.Request.Body, leaveOpen: true); var requestBody = reader.ReadToEndAsync().Result; context.HttpContext.Request.Body.Position = 0; // 执行Schema校验 var jObject = JObject.Parse(requestBody); IList<ValidationError> errors = new List<ValidationError>(); if (!jObject.IsValid(_schema, out errors)) { // 校验失败直接返回400 context.Result = new BadRequestObjectResult(new { Message = "请求JSON格式不符合要求", Errors = errors.Select(e => e.Message) }); return; } base.OnActionExecuting(context); } }
使用方式:直接在需要校验的接口上贴Attribute即可
[HttpPost] [JsonSchemaValidation(nameof(JsonSchemaSettings.CreateUserSchema))] public IActionResult CreateUser([FromBody] CreateUserDto dto) { // 业务逻辑 }
如果需要全局生效所有接口,在Startup的ConfigureServices中注册全局过滤器即可:
services.AddControllers(options => { options.Filters.Add<JsonSchemaValidationAttribute>(); });
中间件实现方案(全局统一校验)
如果你需要全局所有PUT/POST/PATCH请求都走同一套Schema校验,可以用中间件实现:
public class JsonSchemaValidationMiddleware { private readonly RequestDelegate _next; private readonly JsonSchema _globalSchema; // 注入配置读取全局Schema public JsonSchemaValidationMiddleware(RequestDelegate next, IOptions<JsonSchemaSettings> schemaSettings) { _next = next; using var stream = Assembly.GetExecutingAssembly().GetManifestResourceStream(schemaSettings.Value.GlobalSchema); using var reader = new StreamReader(stream); _globalSchema = JsonSchema.Parse(reader.ReadToEnd()); } public async Task InvokeAsync(HttpContext context) { // 仅校验JSON类型的PUT/POST/PATCH请求 var method = context.Request.Method; if (method is HttpMethods.Post or HttpMethods.Put or HttpMethods.Patch && context.Request.ContentType?.Contains("application/json") == true) { context.Request.EnableBuffering(); using var reader = new StreamReader(context.Request.Body, leaveOpen: true); var requestBody = await reader.ReadToEndAsync(); context.Request.Body.Position = 0; var jObject = JObject.Parse(requestBody); IList<ValidationError> errors = new List<ValidationError>(); if (!jObject.IsValid(_globalSchema, out errors)) { context.Response.StatusCode = StatusCodes.Status400BadRequest; context.Response.ContentType = "application/json"; await context.Response.WriteAsJsonAsync(new { Message = "请求JSON不符合Schema要求", Errors = errors.Select(e => e.Message) }); return; } } await _next(context); } }
在Startup的Configure方法中注册中间件,位置放在路由中间件之后、控制器中间件之前即可:
app.UseRouting(); app.UseMiddleware<JsonSchemaValidationMiddleware>(); app.UseAuthorization(); app.MapControllers();
注意事项
- 大文件上传接口不适用该方案,会将完整请求体读入内存,影响性能
- 多Schema场景优先用过滤器方案,可灵活适配不同接口的校验规则
内容的提问来源于stack exchange,提问作者yBother
相关产品推荐
相关产品推荐

