Server.Transfer, Response.Redirect и ApplicationInstance.CompleteRequest работают в ASP.Net - PullRequest
0 голосов
/ 08 марта 2019

Я пытаюсь добавить асинхронные функции в существующее приложение форм asp.net. Но у меня проблемы с передачей / перенаправлением страниц. Причина в том, что большая часть существующего кода перенаправления / передачи выполняются из служебных классов или пользовательских элементов управления и т. Д. Кроме того, есть код, который существует после перенаправления / передачи дальше по стеку, которого следует избегать. Исходный код использовал преимущества перенаправления / передачи, внутренне вызывающего Response.End, и короткого замыкания при выполнении кода для потока через ThreadAbortException.

Для Server.Transfer и Response.Redirect страницы, которые были сделаны асинхронными путем добавления Async = "true", имеют всплывающее сообщение ThreadAbortException, и браузер завершается с ошибкой. Я предполагаю, что, поскольку страница асинхронна, текущий запрос страницы завершается до страницы переноса, а не из-за синхронизации прерывания потока, тогда начнется обработка страницы переноса.

Когда я использую Response.Redirect ("[somepagename]", false) или Server.Execute, они предотвращают завершение текущего ответа, поэтому я добавляю ApplicationInstance.CompleteRequest после них, который пропускает последующую обработку событий и конвейер обработчика. Но код после перенаправления / выполнения продолжается, что вызывает другие ошибки / проблемы. (Например, снова вызывая Response.Redirect, чтобы перейти на другую страницу на основе логики, которая изначально была бы пропущена исключением ThreadAbortException.)

Я также пробовал Response.FlushAsync между Server.Execute и ApplicationInstance.CompleteRequest, который выглядит нормально для браузера. Исключения после CompleteRequest не отображаются в браузере (по крайней мере, в dev), поскольку FlushAsync отправляет буферизованный ответ со страницы передачи. Но это все еще может иметь условие гонки, если выполнение кода сброса или продолжения кода первой страницы заканчивается первым. И последующий код после 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 голосов
/ 09 марта 2019

У меня есть решение.Я изменил часть вызова 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 (), который пропускает дальнейшие события и конвейерные модули.

Таким образом, в случае асинхронного обработчика, CompleteRequest () вызывается внутренне, а не Response.End, исключение ThreadAbortException не генерируется, и код продолжает выполняться после вызова Transfer или Redirect.Мой обработчик событий щелчка для страницы является асинхронным обработчиком пустот, поэтому компилятор делает его асинхронным, и мы получаем асинхронные проблемы Server.Transfer Response.Redirect вниз по течению.

Решение: Server.Execute запускает код страницы, которую ятоже хочу передать и заполняет буфер ответа.Flush сбрасывает буфер в браузер.Предпочитает 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.Преобразование в .NET Core не вариант.

Не то, на что я надеялся в решении.Надеюсь, кто-то более знающий, чем я, есть лучшеЭто не означает, что я записываю половину существующей программы.

...