Server.Transfer,Response.Redirect和ApplicationInstance.CompleteRequest在ASP.Net中可以解决

时间:2019-03-08 01:31:13

标签: c# asp.net async-await response.redirect server.transfer

我试图将异步功能添加到现有的asp.net表单应用程序中。但是我在页面传输/重定向方面遇到了问题。原因是在许多现有代码中,重定向/传输是从实用程序类或自定义控件等中进行的。此外,在重定向/传输之后,还存在一些需要避免的代码。原始代码利用了内部重定向和传输的内部调用Response.End,并通过ThreadAbortException对该线程的代码执行进行了短路。

对于Server.Transfer和Response.Redirect,通过添加Async =“ true”变为异步的页面将出现ThreadAbortException气泡,并且浏览器最终将出现错误响应。我假设由于页面是异步的,当前页面请求在传输页面之前完成,而不是线程中止的同步方式,因此将开始处理传输页面。

当我使用Response.Redirect(“ [somepagename]”,false)或Server.Execute时,它们阻止了当前响应的结束,因此我在它们之后添加了ApplicationInstance.CompleteRequest,从而跳过了以后的事件和处理程序管道处理。但是重定向/执行后的代码仍然继续,这会导致其他错误/问题。 (例如,根据最初被ThreadAbortException跳过的逻辑,再次调用Response.Redirect进入另一个页面。)

我还尝试了Server.Execute和ApplicationInstance.CompleteRequest之间的Response.FlushAsync,这对于浏览器来说还可以。由于FlushAsync从传输页面发送缓冲的响应,因此CompleteRequest之后的异常不会显示给浏览器(至少在开发人员中如此)。但是,关于第一页的刷新或继续执行代码是否首先完成,可能仍然存在竞争条件。并且CompleteRequest之后的后续代码仍会执行,这可能会有副作用。

原始代码:     

public static void GoToPage( string page, bool transfer )
{
    if ( transfer ){HttpContext.Current.Server.Transfer( page, false ); }
    else { HttpContext.Current.Response.Redirect( page ); }
}

1 个答案:

答案 0 :(得分:0)

我有解决办法。我将调用的Server.Transfer部分更改为:

if ( ( HttpContext.Current.Handler as Page )?.IsAsync ?? false )
{

    if ( transfer )
    {
        HttpContext.Current.Server.Execute( page, false );
        HttpContext.Current.Response.FlushAsync();
        Thread.CurrentThread.Abort();
    }
    else
    {
        HttpContext.Current.Response.Redirect( page, false );
        Thread.CurrentThread.Abort();
    }
}
else
{
    if ( transfer )
    {
        HttpContext.Current.Server.Transfer( page, false );
    }
    else
    {
        HttpContext.Current.Response.Redirect( page );
    }
}

然后更新了异步单击偶数处理程序以捕获ThreadAbortException。异常处理程序调用ResetAbort和CompleteRequest。     

private async void SubmitButton_Click( object sender, EventArgs e )
{
    try
    {
        //Awaited async call that returns a value.

        //Call to utility classes that eventually call GoToPage( string page, bool transfer )

    }   
    catch ( ThreadAbortException tEx )
    {
        Thread.ResetAbort();
        HttpContext.Current.ApplicationInstance.CompleteRequest();
    }
}

说明:在ASP.NET中,在同步请求处理程序中,Server.Transfer和Response.Redirect(通过内部调用Response.End的方式)将调用Thread.Abort(触发ThreadAbortException),通过执​​行阻塞调用将请求缓冲区同步刷新到客户端的浏览器。然后调用ApplicationInstance.CompleteRequest(),这将跳过其他事件和管道模块。

因此,在使用异步处理程序的情况下,将在内部而不是Response.End处调用CompleteRequest(),而不会引发ThreadAbortException,并且在调用Transfer或Redirect之后代码将继续执行。我在页面上的click事件处理程序是一个异步void处理程序,因此编译器使其变为异步状态,然后我们获得了异步Server.Transfer Response.Redirect问题。

解决方案: Server.Execute也运行我要传输的页面的代码,并填充响应缓冲区。 FlushAsync异步将缓冲区刷新到浏览器,从而避免了同步刷新。中止将引发ThreadAbortException并绕过其后的代码。 Response.Redirect相似。将endReponse参数设置为false,这样它就不会在内部调用Response.End。然后中止线程以跳过堆栈中的后续代码。

在click事件处理程序中,我将ThreadAbortException压缩在异常处理程序中。 ResetAbort防止在处理程序之后自动重新引发异常。如果没有ResetAbort,则代码将在后续事件中执行。 CompleteRequest跳过事件和http模块,并跳到EndRequest事件。

我确实尝试在asyn部分上使用PageAsyncTask和相关功能来避免异步void单击处理程序,但它仅启动了任务。实际上并没有等待它,因此它结束了执行代码,超出了我需要它返回的值才能完成的代码。

不是我所希望的解决方案。希望比我知识渊博的人有一个更好的人。那并不需要我录制现有程序的一半。