您是否曾經存取過一個網站,它需要很長時間載入,最終你敲擊 F5 重新載入頁面。
即使使用者重新整理了瀏覽器,取消了原始請求, 而對於伺服器來說, API也不會知道它正在計算的值將在結束時被丟棄,重新整理五次,伺服器將觸發 5 個請求。
為了解決這個問題,ASP.NET Core 為 Web 伺服器提供了一種機制,就是CancellationToken.
使用者取消請求時,你可以使用HttpContext.RequestAborted存取,您也可以使用依賴註入將其自動註入到您的操作中。
長時間執行的任務請求
現在我們假設您有一個 API 操作,在向使用者發送響應之前可能需要一些時間才能完成。
在處理該操作時,使用者可以直接取消請求,或重新整理頁面(這會有效地取消原始請求,並啟動新請求)。
[HttpGet(Name = "get")]
public async Task<string> GetAsync()
{
try
{
_logger.LogInformation("request in");
await Task.Delay(5 * 1000);
_logger.LogInformation("request end");
}
catch (Exception ex)
{
_logger.LogInformation("request ex");
}
return "ok";
}
如果使用者在請求中途重新整理瀏覽器,那麽瀏覽器永遠不會收到第一個請求的響應,但在server端可以看到,操作方法執行完成兩次。
這是否是正確將取決於您的應用程式。
如果請求修改某些業務的狀態,那麽您可能不希望在方法中途停止執行。如果請求沒有副作用,那麽您可能希望盡快停止(可能很昂貴)操作。
使用者取消請求時,你可以使用HttpContext.RequestAborted存取,您也可以使用依賴註入將其自動註入到您的操作中。
CancellationTokens取消不必要的請求
以下程式碼顯示了如何透過將 CancellationTokenSource 註入到操作方法中,並透過其取消不必要的操作。
[HttpGet(Name = "get")]
public async Task<string> GetAsync(CancellationToken cancellationToken)
{
try
{
_logger.LogInformation("request in");
await Task.Delay(5 * 1000,cancellationToken);
_logger.LogInformation("request end");
}
catch (Exception ex)
{
_logger.LogInformation("request ex");
}
return "ok";
}
透過這個改變,我們可以再次測試我們的場景。
我們發出一個初始請求,然後我們重新載入頁面。正如您從下面的日誌中看到的,第一個請求不會繼續執行。
使用者重新整理瀏覽器取消請求後不久,原始請求就會中止,並TaskCancelledException透過 API 過濾器管道傳播回來,並備份中介軟體管道。
根據您的場景,您可能能夠依靠此類框架方法來檢查 的狀態CancellationToken,或者您可能需要自己監視取消請求。
過濾器捕獲異常
您可以透過以上try catch 捕獲,或者透過一個過濾器統一監視此異常。
public class OperationCancelledExceptionFilter : ExceptionFilterAttribute
{
private readonly ILogger _logger;
public OperationCancelledExceptionFilter(ILoggerFactory loggerFactory)
{
_logger = loggerFactory.CreateLogger<OperationCancelledExceptionFilter>();
}
public override void OnException(ExceptionContext context)
{
if (context.Exception is OperationCanceledException)
{
_logger.LogInformation("Request was cancelled");
context.ExceptionHandled = true;
context.Result = new StatusCodeResult(400);
}
}
}
builder.Services.AddControllers(options =>
{
options.Filters.Add<OperationCancelledExceptionFilter>();
});
關註我獲取技術分享