Чи вважається Response.End () шкідливим?


198

Ця стаття KB говорить про те, що ASP.NET Response.End()припиняє нитку.

Рефлектор показує, що це виглядає приблизно так:

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();
        }
    }
}

Мені це здається досить суворим. Як йдеться у статті KB, будь-який код у наступному додатку 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
- Корисний аналіз Джона Рейда


2
Поняття не маю, що змінилося після цієї відповіді, але я вловлю Response.End ThreadAbortExceptionпросто чудове.
Маслоу

1
Майте на увазі також, що Response.Redirectі те, і Server.Transferінше дзвоніть, Response.Endі його також слід уникати.
Оуен Блекер

Відповіді:


66

Якщо у вашій програмі був застосований реєстратор винятків, він знищиться ThreadAbortExceptionз цих доброякісних Response.End()дзвінків. Я думаю, що це спосіб Microsoft сказати "Knock it off!".

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


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 / socket і викликає проблеми з такими речами, як KeepAlive.

Рекомендований спосіб закінчення запиту ASP.NET - це HttpApplication.CompleteRequest. Майте на увазі, що візуалізацію ASP.NET потрібно буде пропустити вручну, оскільки HttpApplication.CompleteRequest пропускає решту конвеєра програми IIS / ASP.NET, а не конвеєра сторінки ASP.NET (що є одним етапом у конвеєрі програми).


Код

Copyright © 2001-2007, C6 Software, Inc, як я міг сказати.


Довідково

HttpApplication.CompleteRequest

Викликає ASP.NET обходити всі події та фільтрацію у ланцюзі виконання конвеєра HTTP та безпосередньо виконувати подію EndRequest.

Відповідь.Закінч

Цей метод передбачений лише для сумісності з ASP - тобто для сумісності з технологією веб-програмування на основі COM, що передувала ASP.NET.preceded ASP.NET. [Наголос додано]

Відповідь.Закрийте

Цей метод припиняє з'єднання з клієнтом різко і не призначений для нормальної обробки запитів HTTP . [Наголос додано]


4
> Майте на увазі, що візуалізацію ASP.NET потрібно буде пропустити вручну, оскільки HttpApplication.CompleteRequest пропускає решту конвеєра програми IIS / ASP.NET, а не конвеєра сторінки ASP.NET (який є одним етапом у конвеєрі програми). І як ви це досягаєте?
PilotBob

1
Перегляньте посилання на код, де Джон Рейд продемонстрував, як встановити прапор та замінити методи RaisePostBackEvent та Render сторінки, щоб, за бажанням, пропустити нормальну реалізацію. (Ви, мабуть, зробите це в базовому класі, з якого повинні бути успадковані всі сторінки вашого додатка.) Web.archive.org/web/20101224113858/http://www.c6software.com/…
користувач423430

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 у пошуковій інформації щодо відповіді. . (переосмислення методів візуалізації надмірно складне для такого простого завдання 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 () (бути порожньою) і це вирішило її для мене.
Аерік

1
CompleteRequest пропускає частини конвеєра додатків, але все ще проходить через решту процесу візуалізації сторінки, її не негайну зупинку, як response.end, її більш витончена. Є більш глибокі пояснення того, чому в інших відповідях на цій сторінці.
Джей Зелос

11

На питання "Я все ще не знаю різниці між Response.Close та CompleteRequest ()", я б сказав:

Віддайте перевагу CompleteRequest (), не використовуйте Response.Close ().

Дивіться наступну статтю для добре зробленого резюме цієї справи.

Майте на увазі, що навіть після виклику CompleteRequest () деякий текст (наприклад, змінений з коду ASPX) буде доданий до вихідного потоку відповідей. Ви можете запобігти цьому, замінивши методи Render і RaisePostBackEvent, як описано в наступній статті .

BTW: Я погоджуюся із запобіганням використання Response.End (), особливо під час запису даних у потік http для імітації завантаження файлів. Раніше ми використовували Response.End (), поки наш файл журналу не заповнився ThreadAbortExceptions.


Мене цікавить переосмислення рендеру, як ви описуєте, але посилання на "наступну статтю" мертве. Можливо, ви можете оновити свій запис?
paqogomez

Вибачте за пізню відповідь. Я точно не пам’ятаю, що було в тій статті. Однак я знайшов це на 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 як частину його поточної реалізації (як зазначає ОП).

ThreadAbortException - це особливий виняток, який можна зловити, але він автоматично буде знову піднятий в кінці блоку вилову.

Щоб дізнатися, як написати код, який повинен мати справу з ThreadAbortExceptions, див. Відповідь @ Мехрдада на SO Як я можу виявити тріадаборцепцію в остаточному блоці, де він посилається на RuntimeHelpers.ExecuteCodeWithGuaranteedCleanup метод та обмежені регіони виконання.


Стаття Рік Стрехл згадується повчальна, і не забудьте прочитати коментарі , а також. Зауважимо, що питання Страля було специфічним. Він хотів отримати дані клієнту (зображенню), а потім обробити оновлення бази даних відстеження хітів, що не сповільнило подачу зображення, що спричинило проблему робити щось після того, як було викликано 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, End може спричинити проблеми.


Майте на увазі, Flush () - це не "це кінець". Це просто "змити все досі". Причина, за якою ви хочете "це кінець", полягає в тому, щоб клієнт усвідомлював, що у нього є весь вміст, а сервер може працювати і робити інші речі - оновлювати файл журналу, запитувати лічильник бази даних чи будь-що інше. Якщо ви зателефонуєте на Response.Flush, а потім зробите одну з цих речей, клієнт може продовжувати чекати ще. Якщо ви зателефонуєте на Response.End (), тоді керування вискакує, а БД не отримує запитів тощо.
Cheeso

Можна також використати переопределити Response.Redirect ("....", істина), де bool "endResponse: Вказує, чи має завершитися поточне виконання сторінки"
Роберт Полсон

Завжди краще використовувати рамку автентифікації форм для захисту сторінок, які мають бути захищені обліковими записами для входу.
Роберт Поулсон

3
Насправді, щоб виправити себе, я вважаю, що за замовчуванням Response.Redirect і Server.Transfer - це викликати Response.End внутрішньо, якщо ви не зателефонуєте на переопрацювання та не передасте "false". ,
Роберт Поулсон

1
Response.end працює в .net багато інакше, ніж це робиться в класичному ASP. У .net це спричиняє тріадаборстецепцію, що може бути досить неприємно.
Енді

2

Я використовував лише Response.End () як механізм тестування / налагодження

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

Судячи з того, що ви опублікували з точки зору досліджень, я б сказав, що це було б поганим дизайном, якби він вимагав відповіді. Закінчити


0

У класичному підсилювачі у мене на деяких дзвінках ajax у TTFB (Time To First Byte) тривалістю від 3 до 10 секунд, набагато більше, ніж TTFB на звичайних сторінках із значно більшою кількістю викликів SQL.

Повернувся ajax - це сегмент HTML, який потрібно вводити на сторінку.

TTFB був на кілька секунд довший, ніж час візуалізації.

Якщо я додав відповідь.end після візуалізації, TTFB сильно зменшився.

Я міг би отримати той же ефект, випромінюючи "</body> </html>", але це, ймовірно, не працює при виведенні json або xml; тут потрібна відповідь.end.


Я знаю, що це стара відповідь, але знову ж таки, класичний асп - старий. Я вважаю це корисним ;-)
Leif Neland
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.