{"id":21694,"date":"2019-03-27T13:51:50","date_gmt":"2019-03-27T20:51:50","guid":{"rendered":"https:\/\/devblogs.microsoft.com\/aspnet\/?p=21694"},"modified":"2019-03-27T13:51:50","modified_gmt":"2019-03-27T20:51:50","slug":"re-reading-asp-net-core-request-bodies-with-enablebuffering","status":"publish","type":"post","link":"https:\/\/devblogs.microsoft.com\/dotnet\/re-reading-asp-net-core-request-bodies-with-enablebuffering\/","title":{"rendered":"Re-reading ASP.Net Core request bodies with EnableBuffering()"},"content":{"rendered":"<p>In some scenarios there&#8217;s a need to read the request body multiple times. Some examples include<\/p>\n<ul>\n<li>Logging the raw requests to replay in load test environment<\/li>\n<li>Middleware that read the request body multiple times to process it<\/li>\n<\/ul>\n<p>Usually <code>Request.Body<\/code> does not support rewinding, so it can only be read once. A straightforward solution is to save a copy of the stream in another stream that supports seeking so the content can be read multiple times from the copy.<\/p>\n<p>In ASP.NET framework it was possible to read the body of an HTTP request multiple times using <a href=\"https:\/\/docs.microsoft.com\/en-us\/dotnet\/api\/system.web.httprequest.getbufferedinputstream?view=netframework-4.7.2\"><code>HttpRequest.GetBufferedInputStream<\/code> method<\/a>. However, in ASP.NET Core a different approach must be used.<\/p>\n<p>In ASP.NET Core 2.1 we added <a href=\"https:\/\/docs.microsoft.com\/en-us\/dotnet\/api\/microsoft.aspnetcore.http.httprequestrewindextensions.enablebuffering?view=aspnetcore-2.1\">an extension method <code>EnableBuffering()<\/code><\/a> for <code>HttpRequest<\/code>. This is the suggested way to enable request body for multiple reads. Here is an example usage in the <code>InvokeAsync()<\/code> method of a custom ASP.NET middleware:<\/p>\n<pre><code class=\"csharp\">public async Task InvokeAsync(HttpContext context, RequestDelegate next)\n{\n    context.Request.EnableBuffering();\n\n    \/\/ Leave the body open so the next middleware can read it.\n    using (var reader = new StreamReader(\n        context.Request.Body,\n        encoding: Encoding.UTF8,\n        detectEncodingFromByteOrderMarks: false,\n        bufferSize: bufferSize,\n        leaveOpen: true))\n    {\n        var body = await reader.ReadToEndAsync();\n        \/\/ Do some processing with body\u2026\n\n        \/\/ Reset the request body stream position so the next middleware can read it\n        context.Request.Body.Position = 0;\n    }\n\n    \/\/ Call the next delegate\/middleware in the pipeline\n    await next(context);\n}\n<\/code><\/pre>\n<p>The backing <code>FileBufferingReadStream<\/code> uses memory stream of a certain size first then falls back to a temporary file stream. By default the size of the memory stream is 30KB. There are also other <code>EnableBuffering()<\/code> overloads that allow specifying a different threshold, and\/or a limit for the total size:<\/p>\n<pre><code class=\"csharp\">public static void EnableBuffering(this HttpRequest request, int bufferThreshold)\n\npublic static void EnableBuffering(this HttpRequest request, long bufferLimit)\n\npublic static void EnableBuffering(this HttpRequest request, int bufferThreshold, long bufferLimit)\n<\/code><\/pre>\n<p>For example, a call of<\/p>\n<pre><code class=\"csharp\">context.Request.EnableBuffering(bufferThreshold: 1024 * 45, bufferLimit: 1024 * 100);\n<\/code><\/pre>\n<p>enables a read buffer with limit of 100KB. Data is buffered in memory until the content exceeds 45KB, then it\u2019s moved to a temporary file. By default there&#8217;s no limit on the buffer size but if there&#8217;s one specified and the content of request body exceeds the limit, an <code>System.IOException<\/code> will be thrown.<\/p>\n<p>These overloads offer flexibility if there&#8217;s a need to fine-tune the buffering behaviors. Just keep in mind that:<\/p>\n<ul>\n<li>Even though the memory stream is rented from a pool, it still has memory cost associated with it.<\/li>\n<li>After the read is over the <code>bufferThreshold<\/code> the performance will be slower since a file stream will be used.<\/li>\n<\/ul>\n","protected":false},"excerpt":{"rendered":"<p>This post describes the EnableBuffering() extension method which enable re-reading of ASP.NET Core request body.<\/p>\n","protected":false},"author":25629,"featured_media":58792,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"footnotes":""},"categories":[197],"tags":[],"class_list":["post-21694","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-aspnet"],"acf":[],"blog_post_summary":"<p>This post describes the EnableBuffering() extension method which enable re-reading of ASP.NET Core request body.<\/p>\n","_links":{"self":[{"href":"https:\/\/devblogs.microsoft.com\/dotnet\/wp-json\/wp\/v2\/posts\/21694","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/devblogs.microsoft.com\/dotnet\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/devblogs.microsoft.com\/dotnet\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/dotnet\/wp-json\/wp\/v2\/users\/25629"}],"replies":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/dotnet\/wp-json\/wp\/v2\/comments?post=21694"}],"version-history":[{"count":0,"href":"https:\/\/devblogs.microsoft.com\/dotnet\/wp-json\/wp\/v2\/posts\/21694\/revisions"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/dotnet\/wp-json\/wp\/v2\/media\/58792"}],"wp:attachment":[{"href":"https:\/\/devblogs.microsoft.com\/dotnet\/wp-json\/wp\/v2\/media?parent=21694"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/dotnet\/wp-json\/wp\/v2\/categories?post=21694"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/dotnet\/wp-json\/wp\/v2\/tags?post=21694"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}