What is the lifecycle and/or ownership of the incoming request body stream in ASP.NET Core? And how can I...
In my ASP.NET Core Web API, desktop client software will upload large binary HTTP requests (potentially gigagbytes). I want to process these in the background and return a response to the client if processing fails to complete within a set timeout of a few seconds.
Currently my processing code reads the request stream directly - which means the life-cycle of the request Stream
needs to outlast the Request/Response - however I was not able to find any documentation regarding how to transfer ownership of the Request's stream to prevent it from being Closed/Disposed immediately after the controller response completes.
Here's a basic example:
class BigDataController : ControllerBase
{
[HttpPost("data")]
public async Task<IActionResult> ReceiveData()
{
Task processTask = this.ProcessDataAsync( this.Request.Body );
Task timeoutTask = Task.Delay( 5000 );
Task completed = await Task.WhenAny( processTask, timeoutTask );
if( completed == timeoutTask )
{
return this.OK( "Still processing, please check back later..." );
}
else
{
return this.Ok( "Processing completed." );
}
}
private async Task ProcessDataAsync( Stream stream )
{
using( StreamReader rdr = new StreamReader( stream ) )
{
// etc
}
}
}
In the event that ProcesDataAsync
takes longer than 5 seconds to run the ReceiveData
controller action will return a response to the client and ASP.NET will dispose of the request's body stream. I don't want this to happen, obviously.
While a workaround is to perform a stream copy to a MemoryStream
or other persistent storage and then process that data, I have concerns it will not scale well to the potentially gigabyte-sized requests I expect to receive.
If this is actually by-design, then what proven strategies exist to proxy or otherwise abstract-away the incoming request stream via a persistent buffer to prevent the closure of the request/response pair from interrupting my ProcessDataAsync
method?
asp.net-core
add a comment |
In my ASP.NET Core Web API, desktop client software will upload large binary HTTP requests (potentially gigagbytes). I want to process these in the background and return a response to the client if processing fails to complete within a set timeout of a few seconds.
Currently my processing code reads the request stream directly - which means the life-cycle of the request Stream
needs to outlast the Request/Response - however I was not able to find any documentation regarding how to transfer ownership of the Request's stream to prevent it from being Closed/Disposed immediately after the controller response completes.
Here's a basic example:
class BigDataController : ControllerBase
{
[HttpPost("data")]
public async Task<IActionResult> ReceiveData()
{
Task processTask = this.ProcessDataAsync( this.Request.Body );
Task timeoutTask = Task.Delay( 5000 );
Task completed = await Task.WhenAny( processTask, timeoutTask );
if( completed == timeoutTask )
{
return this.OK( "Still processing, please check back later..." );
}
else
{
return this.Ok( "Processing completed." );
}
}
private async Task ProcessDataAsync( Stream stream )
{
using( StreamReader rdr = new StreamReader( stream ) )
{
// etc
}
}
}
In the event that ProcesDataAsync
takes longer than 5 seconds to run the ReceiveData
controller action will return a response to the client and ASP.NET will dispose of the request's body stream. I don't want this to happen, obviously.
While a workaround is to perform a stream copy to a MemoryStream
or other persistent storage and then process that data, I have concerns it will not scale well to the potentially gigabyte-sized requests I expect to receive.
If this is actually by-design, then what proven strategies exist to proxy or otherwise abstract-away the incoming request stream via a persistent buffer to prevent the closure of the request/response pair from interrupting my ProcessDataAsync
method?
asp.net-core
add a comment |
In my ASP.NET Core Web API, desktop client software will upload large binary HTTP requests (potentially gigagbytes). I want to process these in the background and return a response to the client if processing fails to complete within a set timeout of a few seconds.
Currently my processing code reads the request stream directly - which means the life-cycle of the request Stream
needs to outlast the Request/Response - however I was not able to find any documentation regarding how to transfer ownership of the Request's stream to prevent it from being Closed/Disposed immediately after the controller response completes.
Here's a basic example:
class BigDataController : ControllerBase
{
[HttpPost("data")]
public async Task<IActionResult> ReceiveData()
{
Task processTask = this.ProcessDataAsync( this.Request.Body );
Task timeoutTask = Task.Delay( 5000 );
Task completed = await Task.WhenAny( processTask, timeoutTask );
if( completed == timeoutTask )
{
return this.OK( "Still processing, please check back later..." );
}
else
{
return this.Ok( "Processing completed." );
}
}
private async Task ProcessDataAsync( Stream stream )
{
using( StreamReader rdr = new StreamReader( stream ) )
{
// etc
}
}
}
In the event that ProcesDataAsync
takes longer than 5 seconds to run the ReceiveData
controller action will return a response to the client and ASP.NET will dispose of the request's body stream. I don't want this to happen, obviously.
While a workaround is to perform a stream copy to a MemoryStream
or other persistent storage and then process that data, I have concerns it will not scale well to the potentially gigabyte-sized requests I expect to receive.
If this is actually by-design, then what proven strategies exist to proxy or otherwise abstract-away the incoming request stream via a persistent buffer to prevent the closure of the request/response pair from interrupting my ProcessDataAsync
method?
asp.net-core
In my ASP.NET Core Web API, desktop client software will upload large binary HTTP requests (potentially gigagbytes). I want to process these in the background and return a response to the client if processing fails to complete within a set timeout of a few seconds.
Currently my processing code reads the request stream directly - which means the life-cycle of the request Stream
needs to outlast the Request/Response - however I was not able to find any documentation regarding how to transfer ownership of the Request's stream to prevent it from being Closed/Disposed immediately after the controller response completes.
Here's a basic example:
class BigDataController : ControllerBase
{
[HttpPost("data")]
public async Task<IActionResult> ReceiveData()
{
Task processTask = this.ProcessDataAsync( this.Request.Body );
Task timeoutTask = Task.Delay( 5000 );
Task completed = await Task.WhenAny( processTask, timeoutTask );
if( completed == timeoutTask )
{
return this.OK( "Still processing, please check back later..." );
}
else
{
return this.Ok( "Processing completed." );
}
}
private async Task ProcessDataAsync( Stream stream )
{
using( StreamReader rdr = new StreamReader( stream ) )
{
// etc
}
}
}
In the event that ProcesDataAsync
takes longer than 5 seconds to run the ReceiveData
controller action will return a response to the client and ASP.NET will dispose of the request's body stream. I don't want this to happen, obviously.
While a workaround is to perform a stream copy to a MemoryStream
or other persistent storage and then process that data, I have concerns it will not scale well to the potentially gigabyte-sized requests I expect to receive.
If this is actually by-design, then what proven strategies exist to proxy or otherwise abstract-away the incoming request stream via a persistent buffer to prevent the closure of the request/response pair from interrupting my ProcessDataAsync
method?
asp.net-core
asp.net-core
asked Nov 28 '18 at 10:25
DaiDai
73.7k13118205
73.7k13118205
add a comment |
add a comment |
0
active
oldest
votes
Your Answer
StackExchange.ifUsing("editor", function () {
StackExchange.using("externalEditor", function () {
StackExchange.using("snippets", function () {
StackExchange.snippets.init();
});
});
}, "code-snippets");
StackExchange.ready(function() {
var channelOptions = {
tags: "".split(" "),
id: "1"
};
initTagRenderer("".split(" "), "".split(" "), channelOptions);
StackExchange.using("externalEditor", function() {
// Have to fire editor after snippets, if snippets enabled
if (StackExchange.settings.snippets.snippetsEnabled) {
StackExchange.using("snippets", function() {
createEditor();
});
}
else {
createEditor();
}
});
function createEditor() {
StackExchange.prepareEditor({
heartbeatType: 'answer',
autoActivateHeartbeat: false,
convertImagesToLinks: true,
noModals: true,
showLowRepImageUploadWarning: true,
reputationToPostImages: 10,
bindNavPrevention: true,
postfix: "",
imageUploader: {
brandingHtml: "Powered by u003ca class="icon-imgur-white" href="https://imgur.com/"u003eu003c/au003e",
contentPolicyHtml: "User contributions licensed under u003ca href="https://creativecommons.org/licenses/by-sa/3.0/"u003ecc by-sa 3.0 with attribution requiredu003c/au003e u003ca href="https://stackoverflow.com/legal/content-policy"u003e(content policy)u003c/au003e",
allowUrls: true
},
onDemand: true,
discardSelector: ".discard-answer"
,immediatelyShowMarkdownHelp:true
});
}
});
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
StackExchange.ready(
function () {
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f53517227%2fwhat-is-the-lifecycle-and-or-ownership-of-the-incoming-request-body-stream-in-as%23new-answer', 'question_page');
}
);
Post as a guest
Required, but never shown
0
active
oldest
votes
0
active
oldest
votes
active
oldest
votes
active
oldest
votes
Thanks for contributing an answer to Stack Overflow!
- Please be sure to answer the question. Provide details and share your research!
But avoid …
- Asking for help, clarification, or responding to other answers.
- Making statements based on opinion; back them up with references or personal experience.
To learn more, see our tips on writing great answers.
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
StackExchange.ready(
function () {
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f53517227%2fwhat-is-the-lifecycle-and-or-ownership-of-the-incoming-request-body-stream-in-as%23new-answer', 'question_page');
}
);
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown