中间件读取请求体后,接口触发JSON反序列化400错误的问题
问题原因与解决方案
为什么会出现这个错误?
HttpContext.Request.Body是一个单向、不可重复读取的流。当你在中间件中调用ReadToEndAsync()读取完整个请求体后,流的指针会移动到流的末尾。后续ASP.NET Core的模型绑定器(负责解析[FromBody]参数的组件)尝试读取这个流时,只能读到空内容,自然就会抛出JSON解析失败的错误:
"The input does not contain any JSON tokens. Expected the input to start with a valid JSON token, when isFinalBlock is true.
Path: $ | LineNumber: 0 | BytePositionInLine: 0."
你觉得只读取了一次,但实际上整个请求处理流程中,你的中间件是第一次读取,模型绑定器是第二次读取,所以必须让流支持重复读取。
解决方案:启用请求体缓冲
ASP.NET Core提供了内置的方法让请求体可以被重复读取,推荐使用EnableBuffering()(ASP.NET Core 3.0及以上版本),它会将请求体缓存到内存或磁盘中,让流支持多次读取和位置重置。
修改后的中间件代码示例:
public async Task InvokeAsync(HttpContext context, RequestDelegate next) { // 启用请求体缓冲,允许重复读取 context.Request.EnableBuffering(); // 读取请求体内容 string requestBody = await new StreamReader(context.Request.Body).ReadToEndAsync(); // 重置流的位置到开头,确保后续组件能正常读取 context.Request.Body.Position = 0; if(await CheckIfRequestIsAuthorize(requestBody)) { await next.Invoke(context); } } private async Task<bool> CheckIfRequestIsAuthorize(string requestBody) { Console.WriteLine("Inside method"); dynamic tmp = JsonConvert.DeserializeObject(requestBody); string sessionId = (string)tmp.sessionId; return sessionId == "string"; }
额外优化建议
- 直接将读取到的
requestBody传入校验方法,避免重复操作流,代码更简洁。 - 如果担心大请求体占用过多内存,可以通过配置限制缓冲大小,默认小请求用内存存储,大请求自动转磁盘存储。
- 旧版本ASP.NET Core可以使用
EnableRewind(),功能和EnableBuffering()类似,后者是前者的升级版。
关键注意点
- 读取完流之后必须重置
Position到0,否则后续组件依然读不到内容。 - 不要直接修改原始流的状态而不做缓冲,非缓冲流不支持
Seek操作,手动重置位置会抛出异常。
内容的提问来源于stack exchange,提问作者Mateusz Kaleta
相关产品推荐
相关产品推荐

