Является ли Response.End () вредным?

198

В этой статье базы Response.End()знаний говорится, что ASP.NET прерывает поток.

Отражатель показывает, что это выглядит так:

public void End()
{
    if (this._context.IsInCancellablePeriod)
    {
        InternalSecurityPermissions.ControlThread.Assert();
        Thread.CurrentThread.Abort(new HttpApplication.CancelModuleException(false));
    }
    else if (!this._flushing)
    {
        this.Flush();
        this._ended = true;
        if (this._context.ApplicationInstance != null)
        {
            this._context.ApplicationInstance.CompleteRequest();
        }
    }
}

Это кажется довольно резким для меня. Как говорится в статье базы знаний, любой следующий код в приложении Response.End()не будет выполнен, что нарушает принцип наименьшего удивления. Это почти как Application.Exit()в приложении WinForms. Исключение прерывания потока, вызванное Response.End()не может быть перехвачено, поэтому окружение кода в try... finallyне будет удовлетворять.

Это заставляет меня задуматься, стоит ли мне всегда избегать Response.End().

Может кто-нибудь предложить, когда я должен использовать Response.End(), когда Response.Close()и когда HttpContext.Current.ApplicationInstance.CompleteRequest()?

ссылка: запись в блоге Рика Строля .


Исходя из полученной информации, мой ответ: да, Response.Endвреден , но полезен в некоторых ограниченных случаях.

  • использовать Response.End()в качестве неуловимого броска, чтобы немедленно прекратить действие HttpResponseв исключительных условиях. Может быть полезно и во время отладки. Избегайте Response.End()завершения рутинных ответов .
  • используйте, Response.Close()чтобы немедленно закрыть соединение с клиентом. Согласно этому сообщению в блоге MSDN , этот метод не предназначен для обычной обработки HTTP-запросов. Маловероятно, что у вас будет веская причина для вызова этого метода.
  • использовать CompleteRequest()для завершения обычного запроса. CompleteRequestзаставляет конвейер ASP.NET переходить к EndRequestсобытию после завершения текущего HttpApplicationсобытия. Поэтому, если вы позвоните CompleteRequest, то напишите что-нибудь еще в ответ, запись будет отправлена ​​клиенту.

Изменить - 13 апреля 2011

Дополнительная ясность доступна здесь:
- Полезный пост в блоге MSDN
- Полезный анализ Джон Рид

Cheeso
источник
2
Понятия не имею, что изменилось с этого ответа, но я ловлю Response.End ThreadAbortExceptionпросто отлично.
Маслоу
1
Имейте также в виду, что Response.Redirectи Server.Transferоба звонка Response.Endи следует также избегать.
Оуэн Блэкер

Ответы:

66

Если вы использовали регистратор исключений в своем приложении, оно будет смягчено с помощью ThreadAbortExceptionэтих безобидных Response.End()вызовов. Я думаю, что это способ Microsoft сказать "Сбей это!"

Я бы использовал, только Response.End()если было какое-то исключительное условие и никакие другие действия были невозможны. Может быть, регистрация этого исключения может фактически указывать на предупреждение.

spoulson
источник
107

TL; DR

Первоначально я рекомендовал вам просто заменить все ваши вызовы на [Response.End] вызовами [...] CompleteRequest (), но если вы хотите избежать обработки обратной передачи и рендеринга HTML, вам нужно добавить [.. .] также переопределяет.

Джон Рид , "Окончательный анализ"


За MSDN, Джон Рид и Ален Ренон:

Производительность ASP.NET - управление исключениями - написание кода, исключающего исключения

Методы Server.Transfer, Response.Redirect, Response.End вызывают исключения. Каждый из этих методов внутренне вызывает Response.End. Вызов Response.End, в свою очередь, вызывает исключение ThreadAbortException .

Решение ThreadAbortException

HttpApplication.CompleteRequest () устанавливает переменную, которая заставляет поток пропускать большинство событий в конвейере событий HttpApplication [-] не цепочку событий страницы, а цепочку событий приложения.

...

создайте переменную уровня класса, которая помечает, должна ли страница завершиться, и затем проверьте переменную перед обработкой ваших событий или рендерингом вашей страницы. [...] Я бы порекомендовал просто переопределить методы RaisePostBackEvent и Render

Response.End и Response.Close не используются в обычной обработке запросов, когда важна производительность. Response.End - это удобное, жесткое средство завершения обработки запроса с соответствующим снижением производительности. Response.Close предназначен для немедленного завершения HTTP-ответа на уровне IIS / сокета и вызывает проблемы с такими вещами, как KeepAlive.

Рекомендуемый метод завершения запроса ASP.NET - HttpApplication.CompleteRequest. Помните, что рендеринг ASP.NET придется пропустить вручную, поскольку HttpApplication.CompleteRequest пропускает остальную часть конвейера приложения IIS / ASP.NET, а не конвейер ASP.NET Page (который является одним из этапов конвейера приложения).


Код

Copyright © 2001-2007, C6 Software, Inc, насколько я мог судить.


Ссылка

HttpApplication.CompleteRequest

Заставляет ASP.NET обходить все события и фильтрацию в цепочке выполнения конвейера HTTP и напрямую выполнять событие EndRequest.

Response.End

Этот метод предоставляется только для совместимости с ASP, то есть для совместимости с технологией веб-программирования на основе COM, которая предшествовала ASP.NET.preceded ASP.NET. [Акцент добавлен]

Response.Close

Этот метод прерывает соединение с клиентом внезапным образом и не предназначен для обычной обработки HTTP-запроса . [Акцент добавлен]

user423430
источник
4
> Имейте в виду, что рендеринг ASP.NET придется пропустить вручную, поскольку HttpApplication.CompleteRequest пропускает остальную часть конвейера приложения IIS / ASP.NET, а не конвейер ASP.NET Page (который является одной из стадий в конвейере приложения). И как ты этого достигнешь?
PilotBob
1
См. Ссылку на код, где Джон Рид продемонстрировал, как установить флаг и переопределить методы RaisePostBackEvent и Render страницы, чтобы пропустить нормальную реализацию при желании. (Вы, вероятно , сделать это в базовом классе все страницы вашего приложения должны наследовать.) Web.archive.org/web/20101224113858/http://www.c6software.com/...
user423430
4
Просто повторю: HttpApplication.CompleteRequest не завершает ответ, как Response.End.
Глен Литтл
2
HttpApplication.CompleteRequest также не останавливает поток кода, поэтому последующие строки продолжают работать. Это может не повлиять на то, что видит браузер, но если эти строки выполняют какую-либо другую обработку, это может привести к путанице.
Джошуа Франк
3
Я не могу думать, но веб-формы сломаны по дизайну. Что снижает производительность, вызывая Response.End () или позволяя странице загружать все, а затем подавляя ответ? Я не могу видеть, где Response.End () здесь «более» вреден. Кроме того, Microsoft рассматривает `ThreadAbortedException 'как обычное событие, как видно из этого кода: referenceource.microsoft.com/#System.Web/UI/Page.cs,4875. Одна вещь, которая противостоит Response.End (), - это то, что она может завершиться ошибкой. прерывание ответа, которое может иногда приводить к отображению ответа.
Гасан
99

Этот вопрос появляется в верхней части всех запросов Google по поиску информации на response.end, поэтому для других поисковых запросов, таких как я, которые хотят публиковать CSV / XML / PDF и т. Д. В ответ на событие, не отображая всю страницу ASPX, я так и делаю , (переопределение методов рендеринга слишком сложно для такой простой задачи IMO)

// Add headers for a csv file or whatever
Response.ContentType = "text/csv"
Response.AddHeader("Content-Disposition", "attachment;filename=report.csv")
Response.AddHeader("Pragma", "no-cache")
Response.AddHeader("Cache-Control", "no-cache")

// Write the data as binary from a unicode string
Dim buffer As Byte()
buffer = System.Text.Encoding.Unicode.GetBytes(csv)
Response.BinaryWrite(buffer)

// Sends the response buffer
Response.Flush()

// Prevents any other content from being sent to the browser
Response.SuppressContent = True

// Directs the thread to finish, bypassing additional processing
HttpContext.Current.ApplicationInstance.CompleteRequest()
Джей Зелос
источник
1
Вы не должны использовать страницу APSX для этого. Это много потраченных впустую усилий. Вы должны использовать ASMX или веб-службу, что угодно, кроме страницы ASPX.
mattmanser
19
Это, кажется, ответ с самой простой реализацией. Ключ Response.SuppressContent = True.
Крис Вебер
3
@mattmanser - не всегда легко / лучше / желательно иметь отдельную страницу для различного представления одного и того же ресурса. Подумайте о REST и т. Д. Если клиент указывает, что он хочет csv, xml через заголовок или параметр, этот метод, безусловно, будет лучшим, при этом обеспечивая поддержку html через обычные средства рендеринга asp.net.
Крис Вебер
1
Это не сработало для меня. У меня была страница, которая работала с Response.End (), но с использованием всех видов комбинаций Response.Close (), Response.Flush (). HttpContext.Current.ApplicationInstance.CompleteRequest () и другие другие вещи не работали, если у меня был фильтр GzipStream в ответе. Похоже, происходило то, что страница все еще выводилась вместе с моим файлом. Я наконец переопределил функцию Render () (чтобы она была пустой), и это решило ее для меня.
Aerik
1
CompleteRequest пропускает части конвейера приложения, но все равно будет проходить через оставшуюся часть процесса рендеринга страницы, он не является немедленной остановкой, как response.end, он более изящен. Есть более подробные объяснения, почему в других ответах на этой странице.
Джей Зелос
11

На вопрос «Я до сих пор не знаю разницы между Response.Close и CompleteRequest ()», я бы сказал:

Предпочитаю CompleteRequest (), не используйте Response.Close ().

См. Следующую статью для хорошо сделанного резюме этого случая.

Имейте в виду, что даже после вызова CompleteRequest () некоторый текст (например, выделенный из кода ASPX) будет добавлен к потоку вывода ответа. Вы можете предотвратить это, переопределив методы Render и RaisePostBackEvent, как описано в следующей статье .

Кстати: я согласен с запретом использования Response.End (), особенно при записи данных в поток http для эмуляции загрузки файла. Мы использовали Response.End () в прошлом, пока наш файл журнала не заполнился исключениями ThreadAbortExceptions.

Ян Шотола
источник
Я заинтересован в переопределении Render, как вы описываете, но ссылка на «следующую статью» мертва. Возможно, вы можете обновить свою запись?
Пакогомес
Извините за поздний ответ. Я точно не помню, что было в той статье. Тем не менее, я нашел его на webarchive.org: web.archive.org/web/20101224113858/http://www.c6software.com/…
Ян Шотола
9

Я не согласен с утверждением « Ответ. И это вредно ». Это определенно не вредно. Response.End делает то, что говорит; это заканчивает выполнение страницы. Использование отражателя, чтобы увидеть, как это было реализовано, следует рассматривать только как поучительный.


Моя 2cent Рекомендации
ИЗБЕЖАТЬ с использованием в Response.End()качестве потока управления.
СЛЕДУЕТ использовать, Response.End()если вам нужно остановить выполнение запроса, и помните, что (как правило) * ни один код не будет выполняться после этой точки.


* Response.End()и ThreadAbortException s.

Response.End() генерирует исключение ThreadAbortException как часть его текущей реализации (как отмечено OP).

ThreadAbortException - это особое исключение, которое может быть перехвачено, но оно будет автоматически вызвано снова в конце блока catch.

Чтобы узнать, как написать код, который должен иметь дело с ThreadAbortExceptions, смотрите ответ @ Mehrdad на SO. Как я могу обнаружить threadabortexception в блоке finally, где он ссылается на метод RuntimeHelpers.ExecuteCodeWithGuaranteedCleanup и Области ограниченного выполнения


Упомянутая статья Рика Строля поучительна, и обязательно прочитайте комментарии. Обратите внимание, что проблема Strahl была конкретной. Он хотел передать данные клиенту (изображение), а затем обработать обновление базы данных отслеживания обращений, которое не замедляло обслуживание изображения, что создавало проблему для выполнения чего-либо после вызова Response.End.

Роберт Полсон
источник
Мы видели это сообщение stackoverflow.com/questions/16731745/…, предлагающее использовать Response.SuppressContent = True HttpContext.Current.ApplicationInstance.CompleteRequest () вместо Response.End ()
jazzBox
3

Я никогда не думал об использовании Response.End () для управления потоком программ.

Однако Response.End () может быть полезен, например, при передаче файлов пользователю.

Вы записали файл в ответ и не хотите, чтобы к ответу добавлялось что-либо еще, так как это может повредить ваш файл.

Рыбный пирог
источник
1
Я понимаю необходимость в API, чтобы сказать "ответ завершен". Но Response.End () также прерывает поток. Это суть вопроса. Когда стоит объединить эти две вещи?
Cheeso
2

Я использовал Response.End () как в .NET, так и в Classic ASP для принудительного прекращения работы. Например, я использую его, когда есть определенное количество попыток входа в систему. Или когда защищенная страница принимается от неаутентифицированного входа в систему (грубый пример):

    if (userName == "")
    {
        Response.Redirect("......");
        Response.End();
    }
    else
    {
      .....

При передаче файлов пользователю, который я бы использовал Flush, конец может вызвать проблемы.

Тим Меерс
источник
Имейте в виду, Flush () - это не «это конец». Это просто "очистить все до сих пор". Причина, по которой вы можете захотеть «это конец», состоит в том, чтобы клиент знал, что у него есть весь контент, а сервер может пойти и заняться другими делами - обновить файл журнала, запросить счетчик базы данных или что-то еще. Если вы вызываете Response.Flush и затем делаете одну из этих вещей, клиент может продолжать ждать большего. Если вы вызываете Response.End (), то элемент управления выскакивает, и БД не получает запросы и т. Д.
Cheeso
В качестве альтернативы вы можете использовать переопределение Response.Redirect ("....", true), где bool равен 'endResponse: указывает, должно ли текущее выполнение страницы завершиться "
Роберт Полсон,
Всегда лучше использовать платформу проверки подлинности с помощью форм для защиты страниц, которые должны быть защищены учетными данными для входа.
Роберт Полсон
3
На самом деле, чтобы исправить себя, я считаю, что по умолчанию Response.Redirect и Server.Transfer должны вызывать Response.End внутри, если только вы не вызываете переопределение и не передаете «false». Как пишется ваш код, Response.End никогда не вызывается ,
Роберт Полсон
1
Response.end работает по-разному в .net, чем в классическом ASP. В .net это вызывает исключение потока, которое может быть довольно неприятным.
Энди
2

Я использовал только Response.End () в качестве механизма тестирования / отладки.

<snip>
Response.Write("myVariable: " + myVariable.ToString());
Response.End();
<snip>

Судя по тому, что вы опубликовали с точки зрения исследований, я бы сказал, что это будет плохой дизайн, если он потребует ответа.

Натан Куп
источник
0

На классическом asp у меня был TTFB (время до первого байта) от 3 до 10 секунд на некоторых вызовах ajax, намного больше, чем TTFB на обычных страницах с большим количеством вызовов SQL.

Возвращенный ajax был сегментом HTML, который нужно вставить на страницу.

TTFB был на несколько секунд длиннее, чем время рендеринга.

Если я добавлю response.end после рендера, TTFB будет значительно уменьшен.

Я мог бы получить тот же эффект, выдав «</ body> </ html>», но это, вероятно, не работает при выводе json или xml; здесь response.end нужен.

Лейф Неланд
источник
Я знаю, что это старый ответ, но опять же, классический осел старый. Я нашел это полезным ;-)
Лейф Неланд