簡體   English   中英

如何在使用 multipart/* 請求的 web-api 操作中檢索取消令牌(.Net 5)

[英]How to retrieve cancellation token in web-api action that consumes multipart/* request (.Net 5)

我有一個 web.api 動作,通過流接受多部分/表單數據,所以它沒有任何 arguments:

[HttpPost("api/longrunning")]
[Consumes("multipart/form-data")]
public async Task<IActionResult> LongRunningAsync(){
  string? boundary = MultipartRequestHelper.GetBoundary(
    MediaTypeHeaderValue.Parse(Request.ContentType),
    DefaultFormOptions.MultipartBoundaryLengthLimit);
  var reader = new MultipartReader(boundary, HttpContext.Request.Body);
  MultipartSection? section = await reader.ReadNextSectionAsync(); // <---- exception thrown here if below argument is specified
 // imagine the rest of below link is implemented here
 // https://docs.microsoft.com/en-us/aspnet/core/mvc/models/file-uploads?view=aspnetcore-5.0#upload-large-files-with-streaming
 // tldr: it streams incoming files directly to disk, and some form-data in memory, each part is processed
 // as a stream, so I can't use attributes like [FormData], because that would put everything in memory
 // If I used [FormData] with a poco, and cancellation token as two arguments, it would probably work, but that doesn't support streaming
}

但是,這樣我就沒有任何可用的取消令牌。 所以我嘗試使用默認的.Net支持的取消:

public async Task<IActionResult> LongRunningAsync(CancellationToken cancellationToken){

在普通的 controller 中,這可以正常工作,但是在這種類型的 controller 中,由於 Multipartreader,我得到了一個異常:

Unexpected end of Stream, the content may have already been read by another component. 
   at Microsoft.AspNetCore.WebUtilities.MultipartReaderStream.<ReadAsync>d__36.MoveNext()
   at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
   at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
   at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
   at System.Runtime.CompilerServices.TaskAwaiter`1.GetResult()
   at Microsoft.AspNetCore.WebUtilities.StreamHelperExtensions.<DrainAsync>d__3.MoveNext()
   at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
   at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
   at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
   at Microsoft.AspNetCore.WebUtilities.MultipartReader.<ReadNextSectionAsync>d__20.MoveNext()
   at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
   at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
   at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
   at System.Runtime.CompilerServices.TaskAwaiter`1.GetResult()
   at MyNamespace.MyController.<LongRunningAsync>d__8.MoveNext() in C:\Source\Project\MyController.cs:line 94
   at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
   at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
   at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
   at System.Runtime.CompilerServices.TaskAwaiter`1.GetResult()
   at Microsoft.AspNetCore.Mvc.Infrastructure.ActionMethodExecutor.TaskOfIActionResultExecutor.<Execute>d__0.MoveNext()
   at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
   at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
   at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
   at System.Threading.Tasks.ValueTask`1.get_Result()
   at System.Runtime.CompilerServices.ValueTaskAwaiter`1.GetResult()
   at Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.<<InvokeActionMethodAsync>g__Logged|12_1>d.MoveNext()
   at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
   at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
   at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
   at Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.<<InvokeNextActionFilterAsync>g__Awaited|10_0>d.MoveNext()

那么我該如何解決呢? 通常當框架檢測到請求被取消或放棄時,內置的取消令牌就會觸發。 但在這種情況下,我不能使用它,但我真的很想讓 api 取消請求,這樣它就可以涓涓細流並取消所有適用的操作。

由於 in.Net web api 控制器派生自ControllerBase ,它們都具有HttpContext實例屬性,您可以使用其RequestAborted令牌。 相應的文檔

所以是這樣的:

[HttpPost("api/longrunning")]
[Consumes("multipart/form-data")]
public async Task<IActionResult> LongRunningAsync(){

  var cancellationToken = this.HttpContext.RequestAborted;

  string? boundary = MultipartRequestHelper.GetBoundary(
    MediaTypeHeaderValue.Parse(Request.ContentType),
    DefaultFormOptions.MultipartBoundaryLengthLimit);
  var reader = new MultipartReader(boundary, HttpContext.Request.Body);
  MultipartSection? section = await reader.ReadNextSectionAsync(cancellationToken); // you can already start passing it to methods that support it
// ....
}

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM