asp.net 在Webforms中使用异步任务处理/避免超时异常/取消令牌

r1wp621o  于 2023-05-02  发布在  .NET
关注(0)|答案(1)|浏览(157)

摘要

在ASP中使用异步页面时,如何避免TimeoutException或在TimeoutException之后继续呈现页面?NET WebForms?
如果不能使用PageAsyncTask取消令牌来避免Timeout Exception,那么PageAsyncTask取消令牌有什么作用?

详情

我正在调用一个异步端点来获取要在WebForms页面中显示的结果。大多数情况下,这工作得很好,但是,当端点花费太长时间返回结果时,整个页面将显示黄色屏幕

[TimeoutException: An asynchronous operation exceeded the page timeout.

我不想简单地任意增加Page.AsyncTimeout。相反,我想处理超时,并处理页面的其余部分,为页面的部分显示我自己的消息,这取决于超时的异步任务。
对于构造函数

public PageAsyncTask (Func<System.Threading.CancellationToken,System.Threading.Tasks.Task> handler);

Microsoft文档指出:
ASP.NET将根据网页的@ Page指令中设置的AsyncTimeout属性,向传递给TaskEventHandler委托的CancellationToken对象发送取消信号。当达到AsyncTimeout值时,将发出CancellationToken对象的信号。CancellationToken参数必须传递给TaskEventHandler类的实现所调用的任何异步API。
但是,即使这样做,我仍然得到页面级别TimeoutException
构造函数的另一个重载提供超时处理程序

public PageAsyncTask (System.Web.BeginEventHandler beginHandler, System.Web.EndEventHandler endHandler, System.Web.EndEventHandler timeoutHandler, object state);

然而,似乎没有办法将TasktimeoutHandler一起使用。这似乎意味着处理cancellationToken应该足够了,但显然不是。
此外,TimeoutException似乎是在一个不可能以一种有用的(对我来说)方式实际捕获它的点上抛出的--Page.Error事件似乎太晚了,无法恢复页面的呈现。
这似乎引出了另一个问题,即使用public PageAsyncTask (Func<System.Threading.CancellationToken,System.Threading.Tasks.Task> handler);而不是public PageAsyncTask (Func<System.Threading.Tasks.Task> handler);的意义是什么?

示例代码

  • 默认.aspx*
<%@ Page Async="true" AsyncTimeout="5" Language="C#" AutoEventWireup="true" CodeBehind="Default.aspx.cs" Inherits="Foo.Default" %>
<%-- ... --%>
<form runat="server">
  <asp:Button ID="btnGetInfo" runat="server" Text="Get Info" onclick="btnGetInfo_Click" UseSubmitBehavior="false" />
  <asp:TextBox ID="txtInfo" runat="server" />
  <asp:Label ID="lblError" runat="server" />
<%-- ... --%>
</form>
  • Default. aspx。cs*
namespace Foo
{
    public partial class Default : System.Web.UI.Page
    {
        private readonly DummyService _dummyService = new DummyService();

        protected void btnGetInfo_Click(object sender, EventArgs e)
        {
            txtInfo.Text = "";
            lblError.Text = "";

            async Task btnGetInfoClickAsync(CancellationToken cancellationToken)
            {
                //also tried:
                //cancellationToken.Register(() => lblError.Text = "Timeout, data not retrieved", useSynchronizationContext: true);  

                try
                {
                    string info = await _dummyService.GetInfoAsync(cancellationToken);

                    txtInfo.Text = info;
                }
                catch (TaskCanceledException)
                {
                    lblError.Text = "Timeout, data not retrieved";
                }
            }

            RegisterAsyncTask(new PageAsyncTask(btnGetInfoClickAsync));
        }
    }
}
  • DummyService。cs*
namespace Foo
{
    public class DummyService
    {
        public async Task<string> GetInfoAsync(CancellationToken cancellationToken)
        {
            await Task.Delay(6000, cancellationToken).ConfigureAwait(false);
            return Path.GetRandomFileName();
        }
    }
}
  • 网站配置 *
<configuration>
  <!-- ... -->
  <system.web>
    <httpRuntime targetFramework="4.8">
    <compilation debug="true" targetFramework="4.8">
    <!-- ... -->
  </system.web>
  <!-- ... -->
</configuration>
fykwrbwg

fykwrbwg1#

避免TimeoutException并允许继续处理页面的简单方法是:

  • 使用PageAsyncTask (Func<Task> handler)构造函数而不是PageAsyncTask (Func<CancellationToken, Task> handler)构造函数
  • 提供您自己的取消令牌,在Page.AsyncTimeout之前到期。

这假设你调用的async API接受并正确响应CancellationToken
例如 * 默认。aspx.cs*

namespace Foo
{
    public partial class Default : System.Web.UI.Page
    {
        private readonly DummyService _dummyService = new DummyService();

        private CancellationToken _cancellationToken;

        protected void Page_Load(object sender, EventArgs e)
        {
            _cancellationToken = new CancellationTokenSource((int)Page.AsyncTimeout.TotalMilliseconds - 1000).Token;
        }

        protected void btnGetInfo_Click(object sender, EventArgs e)
        {
            txtInfo.Text = "";
            lblError.Text = "";

            async Task btnGetInfoClickAsync()
            {
                try
                {
                    string info = await _dummyService.GetInfoAsync(_cancellationToken);

                    txtInfo.Text = info;
                }
                catch (TaskCanceledException)
                {
                    lblError.Text = "Timeout, data not retrieved";
                }
            }

            RegisterAsyncTask(new PageAsyncTask(btnGetInfoClickAsync));
        }
    }
}

(我仍然不理解PageAsyncTask (Func<CancellationToken, Task> handler);构造函数的实用程序)

相关问题