如何读取中间件中的分块请求?
How can I read a chunked request in a middleware?
我有一个非常基本的 Asp.net 核心 mvc api。这些请求来自我无法控制的客户。我使用中间件来记录请求的主体。它适用于常规请求,但如果它是分块请求,我将无法读取正文。但是,框架随后可以很好地处理它,并且使用正确解码的有效负载调用我的控制器的操作。如果我在我的中间件中放置一个断点并检查 HttpRequest,我可以到达这个对象,这似乎是我想要阅读的内容:
((Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http.HttpRequestPipeReader)((Microsoft.AspNetCore.Http.DefaultHttpRequest)httpContext.Request).BodyReader)._body
类型为Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http.Http1ChunkedEncodingMessageBody
如果我从我的操作中检查它,我可以看到它已完成并读取了我发送的字节数。 None 虽然可以访问,但我该如何阅读?
Startup.cs
public void ConfigureServices(IServiceCollection services)
{
services.AddControllers();
}
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
//other stuff ommited
app.UseRouting();
app.UseMiddleware<RequestLoggingMiddleware>();
app.UseEndpoints(e => e.MapControllers());
}
RequestLoggingMiddleware.cs
public class RequestLoggingMiddleware
{
private readonly RequestDelegate _next;
public RequestLoggingMiddleware(RequestDelegate next)
{
_next = next;
}
public async Task InvokeAsync(HttpContext httpContext)
{
//httpContext.Request.Body is null
//httpContext.Request.ContentLength is 0
//here in debug I can see that the Http1ChunkedEncodingMessageBody _body hasn't received anything yet
await _next(httpContext);
}
}
MyController.cs
[Route("rest/[action]")]
[ApiController]
public class MyController : Controller
{
[HttpPost]
public ActionResult<string> CreateWidget([FromBody] WidgetModel widgetData)
{
//widgetData is populated correctly
//if I inspect this.Request here, _body is marked as completed and having read 108 bytes
return Ok();
}
}
使用 fiddler 捕获的示例请求
POST http://mywidgetapi.mycompany.com/rest/CreateWidget HTTP/1.1
Transfer-Encoding: chunked
Content-Type: application/json; charset=utf-8
61
{"Name":"logitech","Type":"mouse","id":"A1B2"}
0
据我所知,如果数据以一系列块的形式发送。
在这种情况下省略了Content-Length header并且在每个块的开头你需要添加当前块的十六进制格式的长度,然后是'\r\n ' 然后是块本身,然后是另一个 '\r\n'。
终止块是一个常规块,但它的长度为零。紧随其后的是预告片,它由实体 header 字段的(可能为空)序列组成。
所以如果你想在中间件内部获取请求body,你可以参考下面的代码:
app.Use(async (context, next) => {
context.Request.EnableBuffering();
// Leave the body open so the next middleware can read it.
using (var reader = new StreamReader(
context.Request.Body,
encoding: Encoding.UTF8,
detectEncodingFromByteOrderMarks: false,
bufferSize: 1000000,
leaveOpen: true))
{
var body = await reader.ReadToEndAsync();
// Do some processing with body…
// Reset the request body stream position so the next middleware can read it
context.Request.Body.Position = 0;
}
//You could find the contentlength is null
var re2 = context.Request.ContentLength;
await next();
});
结果:
我有一个非常基本的 Asp.net 核心 mvc api。这些请求来自我无法控制的客户。我使用中间件来记录请求的主体。它适用于常规请求,但如果它是分块请求,我将无法读取正文。但是,框架随后可以很好地处理它,并且使用正确解码的有效负载调用我的控制器的操作。如果我在我的中间件中放置一个断点并检查 HttpRequest,我可以到达这个对象,这似乎是我想要阅读的内容:
((Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http.HttpRequestPipeReader)((Microsoft.AspNetCore.Http.DefaultHttpRequest)httpContext.Request).BodyReader)._body
类型为Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http.Http1ChunkedEncodingMessageBody
如果我从我的操作中检查它,我可以看到它已完成并读取了我发送的字节数。 None 虽然可以访问,但我该如何阅读?
Startup.cs
public void ConfigureServices(IServiceCollection services)
{
services.AddControllers();
}
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
//other stuff ommited
app.UseRouting();
app.UseMiddleware<RequestLoggingMiddleware>();
app.UseEndpoints(e => e.MapControllers());
}
RequestLoggingMiddleware.cs
public class RequestLoggingMiddleware
{
private readonly RequestDelegate _next;
public RequestLoggingMiddleware(RequestDelegate next)
{
_next = next;
}
public async Task InvokeAsync(HttpContext httpContext)
{
//httpContext.Request.Body is null
//httpContext.Request.ContentLength is 0
//here in debug I can see that the Http1ChunkedEncodingMessageBody _body hasn't received anything yet
await _next(httpContext);
}
}
MyController.cs
[Route("rest/[action]")]
[ApiController]
public class MyController : Controller
{
[HttpPost]
public ActionResult<string> CreateWidget([FromBody] WidgetModel widgetData)
{
//widgetData is populated correctly
//if I inspect this.Request here, _body is marked as completed and having read 108 bytes
return Ok();
}
}
使用 fiddler 捕获的示例请求
POST http://mywidgetapi.mycompany.com/rest/CreateWidget HTTP/1.1
Transfer-Encoding: chunked
Content-Type: application/json; charset=utf-8
61
{"Name":"logitech","Type":"mouse","id":"A1B2"}
0
据我所知,如果数据以一系列块的形式发送。
在这种情况下省略了Content-Length header并且在每个块的开头你需要添加当前块的十六进制格式的长度,然后是'\r\n ' 然后是块本身,然后是另一个 '\r\n'。
终止块是一个常规块,但它的长度为零。紧随其后的是预告片,它由实体 header 字段的(可能为空)序列组成。
所以如果你想在中间件内部获取请求body,你可以参考下面的代码:
app.Use(async (context, next) => {
context.Request.EnableBuffering();
// Leave the body open so the next middleware can read it.
using (var reader = new StreamReader(
context.Request.Body,
encoding: Encoding.UTF8,
detectEncodingFromByteOrderMarks: false,
bufferSize: 1000000,
leaveOpen: true))
{
var body = await reader.ReadToEndAsync();
// Do some processing with body…
// Reset the request body stream position so the next middleware can read it
context.Request.Body.Position = 0;
}
//You could find the contentlength is null
var re2 = context.Request.ContentLength;
await next();
});
结果: