Web API合规请求管控:替换415状态码及异常返回逻辑优化咨询
嘿,我明白你想要实现的是一个极度严格的Web API——只接受完全符合指定顺序的请求(包括请求头排序、请求体字段排序),不合规的直接返回404。默认的ASP.NET Core绑定逻辑确实不会校验这些顺序,给你一套落地的解决方案:
一、校验请求头顺序
ASP.NET Core默认不会管请求头的顺序,毕竟HTTP标准里请求头是无序的,但你要硬卡顺序的话,写个中间件就能搞定:
1. 编写请求头校验中间件
public class RequestHeaderOrderMiddleware { private readonly RequestDelegate _next; private readonly List<string> _requiredHeaderOrder; public RequestHeaderOrderMiddleware(RequestDelegate next, List<string> requiredOrder) { _next = next; // 统一转小写,避免大小写匹配问题 _requiredHeaderOrder = requiredOrder.Select(h => h.ToLowerInvariant()).ToList(); } public async Task InvokeAsync(HttpContext context) { // 获取请求里实际的头键顺序(按服务器接收顺序) var incomingHeaders = context.Request.Headers.Select(h => h.Key.ToLowerInvariant()).ToList(); // 对比是否和要求的顺序完全匹配 var isValid = _requiredHeaderOrder.SequenceEqual(incomingHeaders.Take(_requiredHeaderOrder.Count)); if (!isValid) { context.Response.StatusCode = 404; await context.Response.WriteAsync("Invalid request header sequence"); return; } // 校验通过,继续处理请求 await _next(context); } } // 给IApplicationBuilder加扩展方法,方便注册中间件 public static class HeaderOrderMiddlewareExtensions { public static IApplicationBuilder UseHeaderOrderValidation(this IApplicationBuilder app, List<string> requiredOrder) { return app.UseMiddleware<RequestHeaderOrderMiddleware>(requiredOrder); } }
2. 在Program.cs中注册中间件
记得把中间件放在路由之前,确保请求进来第一时间就被校验:
var builder = WebApplication.CreateBuilder(args); builder.Services.AddControllers(); var app = builder.Build(); // 替换成你要求的请求头顺序,比如["Content-Type", "Authorization", "X-App-Key"] var requiredHeaders = new List<string> { "Content-Type", "Authorization" }; app.UseHeaderOrderValidation(requiredHeaders); app.UseRouting(); app.UseAuthorization(); app.MapControllers(); app.Run();
二、校验请求体字段顺序
默认的[FromBody]绑定会直接忽略JSON字段的顺序(毕竟JSON规范里字段是无序的),所以得自定义输入格式化器来手动校验:
1. 自定义JSON输入格式化器
这个格式化器会先读取原始JSON,校验字段顺序,再交给默认逻辑解析:
public class OrderedJsonInputFormatter : SystemTextJsonInputFormatter { private readonly List<string> _requiredPropertyOrder; public OrderedJsonInputFormatter(JsonOptions options, List<string> propOrder) : base(options) { _requiredPropertyOrder = propOrder; } public override async Task<InputFormatterResult> ReadRequestBodyAsync(InputFormatterContext context, Encoding encoding) { // 读取原始请求体字符串 using var reader = new StreamReader(context.HttpContext.Request.Body, encoding); var rawJson = await reader.ReadToEndAsync(); // 解析JSON,获取实际的字段顺序 var jsonDoc = JsonDocument.Parse(rawJson); var incomingProps = jsonDoc.RootElement.EnumerateObject().Select(p => p.Name).ToList(); // 对比顺序是否符合要求 var isValid = _requiredPropertyOrder.SequenceEqual(incomingProps.Take(_requiredPropertyOrder.Count)); if (!isValid) { context.ModelState.AddModelError(string.Empty, "Invalid request body property sequence"); return await InputFormatterResult.FailureAsync(context.ModelState); } // 重置请求流(因为已经读过一次了,得让默认解析逻辑能重新读取) context.HttpContext.Request.Body = new MemoryStream(encoding.GetBytes(rawJson)); // 交给默认的JSON解析逻辑处理 return await base.ReadRequestBodyAsync(context, encoding); } } // 扩展方法方便注册格式化器 public static class OrderedJsonFormatterExtensions { public static IMvcBuilder AddOrderedJsonFormatter(this IMvcBuilder builder, List<string> propOrder) { builder.AddMvcOptions(options => { // 替换默认的SystemTextJsonInputFormatter var defaultFormatter = options.InputFormatters.OfType<SystemTextJsonInputFormatter>().FirstOrDefault(); if (defaultFormatter != null) { options.InputFormatters.Remove(defaultFormatter); options.InputFormatters.Add(new OrderedJsonInputFormatter(options.JsonOptions, propOrder)); } }); return builder; } }
2. 注册格式化器
全局生效(所有控制器)
// Program.cs builder.Services.AddControllers() // 替换成你要求的请求体字段顺序,比如["UserId", "UserName", "Email"] .AddOrderedJsonFormatter(new List<string> { "Id", "Name", "Description" });
仅特定控制器/方法生效
如果只需要给某个接口做校验,可以创建针对特定模型的格式化器,或者在控制器上用[Consumes]指定(全局注册更简单直接)。
三、统一返回404
上面的中间件已经在头校验失败时返回404了,请求体校验失败时可以加个全局过滤器统一返回404:
public class ValidationTo404Filter : IActionFilter { public void OnActionExecuting(ActionExecutingContext context) { if (!context.ModelState.IsValid) { context.Result = new NotFoundResult(); } } public void OnActionExecuted(ActionExecutedContext context) { } } // 在Program.cs注册过滤器 builder.Services.AddControllers(options => { options.Filters.Add<ValidationTo404Filter>(); });
一些重要提醒
- HTTP标准里请求头是无序的,强制校验顺序可能会导致一些合法请求被拦截(比如某些代理服务器会重排请求头),一定要确认业务真的需要这么严格的限制。
- JSON规范里字段也是无序的,硬卡顺序不符合JSON的设计初衷,建议再评估下必要性。
- 自定义格式化器时记得重置请求流,不然默认解析逻辑会读不到数据。
内容的提问来源于stack exchange,提问作者nevsnirG
相关产品推荐
相关产品推荐

