简体   繁体   English

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

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

I am trying to add async features to an existing asp.net forms application. 我试图将异步功能添加到现有的asp.net表单应用程序中。 But I am running into problems with page transfer/redirection. 但是我在页面传输/重定向方面遇到了问题。 Reason being in much of the existing code redirects/transfers are made from utility classes or custom controls etc. Also, have code that exists after the redirect/transfer further up the stack that needs to be avoided. 原因是在许多现有代码中,重定向/传输是从实用程序类或自定义控件等中进行的。此外,在重定向/传输之后,还存在一些需要避免的代码。 Original code took advantage of redirect/transfer internally calling Response.End and short circuiting out of the code execution for the thread via the ThreadAbortException. 原始代码利用了在内部调用Response.End的重定向/传输的优势,并通过ThreadAbortException对线程的代码执行进行了短路。

For Server.Transfer and Response.Redirect, pages that have been made asynchronous by adding Async="true" have the ThreadAbortException bubble up and the browser ends up with an error response. 对于Server.Transfer和Response.Redirect,通过添加Async =“ true”进行异步处理的页面将出现ThreadAbortException气泡,并且浏览器最终将出现错误响应。 I am assuming that since the page is async the current page request finishes before the transfer page, instead of the sync way of thread abort, then would start processing the transfer page. 我假设由于页面是异步的,当前页面请求在传输页面之前完成,而不是线程中止的同步方式,因此将开始处理传输页面。

When I use Response.Redirect( "[somepagename]", false) or Server.Execute, they prevent the current response from ending so I add ApplicationInstance.CompleteRequest after them, which skips later event and handler pipeline processing. 当我使用Response.Redirect(“ [somepagename]”,false)或Server.Execute时,它们阻止了当前响应结束,因此我在它们之后添加了ApplicationInstance.CompleteRequest,从而跳过了以后的事件和处理程序管道处理。 But code after the redirect/execute continues which causes other errors/issues. 但是重定向/执行后的代码仍然继续,这会导致其他错误/问题。 (For example calling to Response.Redirect again to go to a different page based on logic that would have been skipped originally by the ThreadAbortException.) (例如,根据最初被ThreadAbortException跳过的逻辑,再次调用Response.Redirect进入另一个页面。)

I also tried Response.FlushAsync between Server.Execute and ApplicationInstance.CompleteRequest which looks ok to the browser. 我还尝试了Server.Execute和ApplicationInstance.CompleteRequest之间的Response.FlushAsync,它对浏览器来说还可以。 Exceptions after CompleteRequest don't show to the browser (at least in dev) since FlushAsync sends the buffered response from the transfer page. 由于FlushAsync从传输页面发送缓冲的响应,因此CompleteRequest之后的异常不会显示给浏览器(至少在开发人员中如此)。 But that still might have race condition as to if the flush or continued code execution of the first page finish first. 但是,关于第一页的刷新或继续执行代码是否首先完成,可能仍然存在竞争条件。 And subsequent code after CompleteRequest is still executed which may have side effects. 并且CompleteRequest之后的后续代码仍会执行,这可能会有副作用。

Original code: 原始代码:

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

I have a solution. 我有解决办法。 I changed the Server.Transfer portion of the call to: 我将调用的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 );
    }
}

Then updated the async click even handler to catch the ThreadAbortException. 然后更新异步单击甚至处理程序以捕获ThreadAbortException。 Exception handler calls ResetAbort and CompleteRequest. 异常处理程序调用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();
    }
}

Explanation: In ASP.NET, in a synchronous request handler, Server.Transfer and Response.Redirect (by way of internally calling Response.End) will call Thread.Abort (trigger the ThreadAbortException), do a blocking call by doing a synchronous flush of the request buffer to the client's browser. 说明:在ASP.NET中,在同步请求处理程序中,Server.Transfer和Response.Redirect(通过内部调用Response.End的方式)将调用Thread.Abort(触发ThreadAbortException),通过执​​行同步刷新来进行阻塞调用请求缓冲区发送给客户端的浏览器。 Then call ApplicationInstance.CompleteRequest() which skips further events and pipeline modules. 然后调用ApplicationInstance.CompleteRequest(),这将跳过其他事件和管道模块。

So in the case of an async handler, CompleteRequest() is called internally instead of Response.End, the ThreadAbortException is not thrown and code continues to execute after Transfer or Redirect is called. 因此,在使用异步处理程序的情况下,将在内部而不是Response.End处调用CompleteRequest(),而不会引发ThreadAbortException,并且在调用Transfer或Redirect之后代码将继续执行。 My click event handler for the page is an async void handler so the compiler makes it async and we get the async Server.Transfer Response.Redirect issues downstream. 我在页面上的click事件处理程序是一个异步void处理程序,因此编译器使其变为异步状态,并在下游获得异步的Server.Transfer Response.Redirect问题。

Solution: Server.Execute runs the code of the page I want to transfer too and populates the response buffer. 解决方案:Server.Execute也运行我要传输的页面的代码,并填充响应缓冲区。 Flush flushes the buffer to the browser. 刷新将缓冲区刷新到浏览器。 Would prefer FlushAsync, avoiding the synchronous flush, but the containing method is not async. 希望使用FlushAsync,避免同步刷新,但是包含方法不是异步的。 Abort throws the ThreadAbortException and bypasses the code after it. 中止将引发ThreadAbortException并绕过其后的代码。 Response.Redirect is similar. Response.Redirect相似。 Set the endReponse parameter to false so it doesn't call Response.End internally. 将endReponse参数设置为false,这样它就不会在内部调用Response.End。 Then Abort the thread to skip subsequent code after it in the stack. 然后中止线程以跳过堆栈中的后续代码。

In the click event handler I squash the ThreadAbortException in the exception handler. 在单击事件处理程序中,我将ThreadAbortException压缩在异常处理程序中。 ResetAbort prevents the exception from being automatically re-thrown after the handler. ResetAbort防止在处理程序之后自动重新引发异常。 Without ResetAbort, code was executing in the subsequent events. 如果没有ResetAbort,则代码将在后续事件中执行。 SuppressContent prevents both the current and transfer pages content from being displayed to the user at the same time. SuppressContent防止当前页面和传输页面的内容同时显示给用户。 This happened in some situations depending on where the async code was. 这在某些情况下会发生,具体取决于异步代码的位置。 CompleteRequest skips events and http modules and jumps ahead to the EndRequest event. CompleteRequest跳过事件和http模块,并跳到EndRequest事件。

I did attempt to use PageAsyncTask and related features on the async portion to avoid the async void click handler, but it only kicks off the task. 我确实尝试在异步部分上使用PageAsyncTask和相关功能来避免异步无效单击处理程序,但它只能启动任务。 It doesn't actually get awaited so it ends up executing code past where I need the value returned by it before it completes. 实际上并没有等待它,因此它结束了执行代码,超出了我需要它返回的值才能完成的代码。 Putting the whole event handler code in PageAsyncTask also didn't work since I need it to execute in the event handler and not later. 将整个事件处理程序代码放在PageAsyncTask中也没有用,因为我需要它在事件处理程序中执行,而不是稍后执行。 ExecuteRegisteredAsyncTasks only works on the legacy asp.net synchronization context. ExecuteRegisteredAsyncTasks仅适用于旧版asp.net同步上下文。 Doesn't work with AspNetSynchronizationContext. 不适用于AspNetSynchronizationContext。 Atleast not in .NET 4.6.1. .NET 4.6.1中没有Atleast。 Converting to .NET Core is not an option. 不能转换为.NET Core。

Not what I was hoping for in a solution. 不是我所希望的解决方案。 Hopefully someone more knowledgeable than I has a better one. 希望比我知识渊博的人有一个更好的人。 That doesn't involve me recording half the existing program. 那并不需要我录制现有程序的一半。

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

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