-
Notifications
You must be signed in to change notification settings - Fork 161
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Write the HTTP response only when all output has been formatted #858
base: release-8.x
Are you sure you want to change the base?
Conversation
This might resolve: #704 |
@microsoft-github-policy-service agree |
@@ -150,6 +151,7 @@ public static ODataSerializerContext BuildSerializerContext(HttpRequest request) | |||
} | |||
|
|||
await serializer.WriteObjectAsync(value, type, messageWriter, writeContext).ConfigureAwait(false); | |||
await request.HttpContext.Response.Body.WriteAsync(responseBody.ToArray()); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
do we really need to create array of unknown size here?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
i used responseBody.CopyTo(request.HttpContext.Response.Body)
initially, but it didn't work how I expected so I resorted to this. I'll find a better way
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ha, I was missing rewinding the buffer response body stream back to position zero
@@ -67,19 +67,20 @@ public static ODataSerializerContext BuildSerializerContext(HttpRequest request) | |||
|
|||
ODataPath path = request.ODataFeature().Path; | |||
IEdmNavigationSource targetNavigationSource = path.GetNavigationSource(); | |||
HttpResponse response = request.HttpContext.Response; | |||
var responseBody = new MemoryStream(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Assume to use memory stream pooling
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do you mean Microsoft.IO.RecyclableMemoryStream ?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@wedgberto -- thanks for your contribution!
Note that we do not want to buffer in memory by default -- this can have a significant performance impact for large responses, and we do everything possible to make sure that we are able to stream a response without buffering.
In order to support efficient streaming, the OData protocol specifically describes instream error scenarios, calling for the response body to be malformed JSON.
So rather than return an appropriate HTTP error status code, the spec states to return a 200 OK with a malformed JSON response body. If that is the case then I guess this PR is obsolete and can be aborted. We'll just have to code our Angular consumer to compensate and not assume that 200 OK responses contain valid JSON. There are other issues in this repo that mention incomplete JSON in the response that might find this specified behaviour useful. #760 #219 #109 |
@@ -21,6 +21,7 @@ | |||
using Microsoft.AspNetCore.OData.Formatter.Value; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Could we add a test for this functionality ?
Purpose
This PR "buffers" the OData message wrapper in a memory stream until the OData query has been enumerated, then copies it into the HTTP response body only when no exception has been thrown.
Scenario
A WebAPI endpoint is serviced by an Entity Framework 6.0 IQueryable.
When the SQL query is executed but the configured SQL command timeout is exceeded, our exception handling middleware successfully catches the SqlException, but is unable to set the HTTP status code to 500 because the HTTP response body is already started.
The endpoint returns a 200 OK HTTP status code and a body containing malformed JSON: