繁体   English   中英

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

[英]Server.Transfer, Response.Redirect, and ApplicationInstance.CompleteRequest work around in ASP.Net

我试图将异步功能添加到现有的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 ); }
}

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

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

    if ( transfer )
    {
        HttpContext.Current.Server.Execute( page, false );
        HttpContext.Current.Response.Flush();
        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.Response.SuppressContent = true;
        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。 然后中止线程以跳过堆栈中的后续代码。

在单击事件处理程序中,我将ThreadAbortException压缩在异常处理程序中。 ResetAbort防止在处理程序之后自动重新引发异常。 如果没有ResetAbort,则代码将在后续事件中执行。 SuppressContent防止当前页面和传输页面的内容同时显示给用户。 这在某些情况下会发生,具体取决于异步代码的位置。 CompleteRequest跳过事件和http模块,并跳到EndRequest事件。

我确实尝试在异步部分上使用PageAsyncTask和相关功能来避免异步无效单击处理程序,但它只能启动任务。 实际上并没有等待它,因此它结束了执行代码,超出了我需要它返回的值才能完成的代码。 将整个事件处理程序代码放在PageAsyncTask中也没有用,因为我需要它在事件处理程序中执行,而不是稍后执行。 ExecuteRegisteredAsyncTasks仅适用于旧版asp.net同步上下文。 不适用于AspNetSynchronizationContext。 .NET 4.6.1中没有Atleast。 不能转换为.NET Core。

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

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM