Czy Response.End () jest uważane za szkodliwe?

198

Ten artykuł KB mówi, że ASP.NET Response.End()przerywa wątek.

Odbłyśnik pokazuje, że wygląda to tak:

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

Wydaje mi się to dość trudne. Jak mówi artykuł KB, żaden kod w poniższej aplikacji Response.End()nie zostanie wykonany, co narusza zasadę najmniejszego zdziwienia. To prawie jak Application.Exit()w aplikacji WinForms. Wyjątek przerwania wątku spowodowany przez Response.End()nie jest możliwy do złapania, więc otoczenie kodu w try... finallynie spełni.

Zastanawiam się, czy zawsze powinienem tego unikać Response.End().

Czy ktoś może zasugerować, kiedy powinienem użyć Response.End(), kiedy Response.Close()i kiedy HttpContext.Current.ApplicationInstance.CompleteRequest()?

ref: wpis na blogu Ricka Strahla .


Na podstawie otrzymanych informacji moja odpowiedź brzmi: tak, Response.Endjest szkodliwa , ale jest przydatna w niektórych ograniczonych przypadkach.

  • użyć Response.End()jako rzut niemożliwy do złapania, aby natychmiast zakończyć HttpResponsewyjątkowe warunki. Może być przydatny również podczas debugowania. Unikaj Response.End()rutynowych odpowiedzi .
  • użyj, Response.Close()aby natychmiast zamknąć połączenie z klientem. Zgodnie z tym postem na blogu MSDN ta metoda nie jest przeznaczona do normalnego przetwarzania żądań HTTP. Jest bardzo mało prawdopodobne, że powinieneś nazwać tę metodę.
  • użyj, CompleteRequest()aby zakończyć normalne żądanie. CompleteRequestpowoduje, że potok ASP.NET przeskakuje do EndRequestzdarzenia po zakończeniu bieżącego HttpApplicationzdarzenia. Więc jeśli zadzwonisz CompleteRequest, a następnie napisz coś więcej w odpowiedzi, zapis zostanie wysłany do klienta.

Edycja - 13 kwietnia 2011 r

Dalsza przejrzystość jest dostępna tutaj:
- Przydatny post na blogu MSDN
- Przydatna analiza autorstwa Jona Reida

Cheeso
źródło
2
nie mam pojęcia, co się zmieniło od tej odpowiedzi, ale dobrze rozumiem Response.End ThreadAbortException.
Masłów
1
Należy również pamiętać, że Response.Redirecti Server.Transferzarówno połączenia Response.Endi należy również unikać.
Owen Blacker

Odpowiedzi:

66

Jeśli w swojej aplikacji zastosowano rejestrator wyjątków, zostanie on rozwodniony za pomocą ThreadAbortExceptions tych łagodnych Response.End()połączeń. Myślę, że w ten sposób Microsoft mówi „Odrzuć to!”.

Używałbym tylko, Response.End()gdyby istniał jakiś wyjątkowy warunek i żadne inne działanie nie było możliwe. Może wtedy zarejestrowanie tego wyjątku może faktycznie oznaczać ostrzeżenie.

spoulson
źródło
107

TL; DR

Początkowo zalecałem, aby po prostu zastąpić wszystkie wywołania [Response.End] wywołaniami [...] CompleteRequest (), ale jeśli chcesz uniknąć przetwarzania zwrotnego i renderowania HTML, musisz dodać [ .] również zastępuje.

Jon Reid , „Analiza końcowa”


Według MSDN, Jon Reid i Alain Renon:

Wydajność ASP.NET - Zarządzanie wyjątkami - Napisz kod, który omija wyjątki

Wszystkie metody Server.Transfer, Response.Redirect, Response.End zgłaszają wyjątki. Każda z tych metod wywołuje wewnętrznie odpowiedź. Wywołanie Response.End z kolei powoduje wyjątek ThreadAbortException .

Rozwiązanie ThreadAbortException

HttpApplication.CompleteRequest () ustawia zmienną, która powoduje, że wątek przeskakuje przez większość zdarzeń w potoku zdarzeń HttpApplication [-] nie łańcuch zdarzeń Page, ale łańcuch zdarzeń Application.

...

utwórz zmienną na poziomie klasy, która będzie oznaczać, czy strona powinna się zakończyć, a następnie sprawdź zmienną przed przetworzeniem zdarzeń lub renderowaniem strony. [...] Polecam po prostu przesłonięcie metod RaisePostBackEvent i Render

Response.End i Response.Close nie są używane w normalnym przetwarzaniu żądań, gdy ważna jest wydajność. Response.End to wygodny, ciężki sposób na zakończenie przetwarzania żądania z powiązanym ograniczeniem wydajności. Odpowiedź.Zamknij służy do natychmiastowego zakończenia odpowiedzi HTTP na poziomie IIS / gniazda i powoduje problemy z takimi rzeczami, jak KeepAlive.

Zalecaną metodą zakończenia żądania ASP.NET jest HttpApplication.CompleteRequest. Należy pamiętać, że renderowanie ASP.NET będzie musiało zostać pominięte ręcznie, ponieważ HttpApplication.CompleteRequest pomija resztę potoku aplikacji IIS / ASP.NET, a nie potok strony ASP.NET (który jest jednym z etapów potoku aplikacji).


Kod

Copyright © 2001-2007, C6 Software, Inc, co mogłem najlepiej powiedzieć.


Odniesienie

HttpApplication.CompleteRequest

Powoduje, że ASP.NET pomija wszystkie zdarzenia i filtruje w łańcuchu wykonawczym potoku HTTP i bezpośrednio wykonuje zdarzenie EndRequest.

Odpowiedź. Koniec

Ta metoda jest dostępna tylko dla kompatybilności z ASP - to znaczy dla kompatybilności z technologią programowania WWW opartą na COM, która poprzedza ASP.NET.preceded ASP.NET. [Podkreślenie dodane]

Odpowiedź.Zamknij

Ta metoda przerywa połączenie z klientem w sposób nagły i nie jest przeznaczona do normalnego przetwarzania żądań HTTP . [Podkreślenie dodane]

użytkownik423430
źródło
4
> Należy pamiętać, że renderowanie ASP.NET będzie musiało zostać pominięte ręcznie, ponieważ HttpApplication.CompleteRequest pomija resztę potoku aplikacji IIS / ASP.NET, a nie potok strony ASP.NET (który jest jednym z etapów w potoku aplikacji). Jak to osiągasz?
PilotBob 18.10.12
1
Zobacz link do kodu, w którym Jon Reid pokazał, jak ustawić flagę i przesłonić metody RaisePostBackEvent i Render strony, aby w razie potrzeby pominąć normalną implementację. (Prawdopodobnie zrobiłbyś to w klasie podstawowej, z której wszystkie strony aplikacji powinny dziedziczyć.) Web.archive.org/web/20101224113858/http://www.c6software.com/…
user423430
4
Po prostu ponownie: HttpApplication.CompleteRequest nie przerywa odpowiedzi, jak robi to Response.End.
Glen Little,
2
HttpApplication.CompleteRequest również nie zatrzymuje przepływu kodu, więc kolejne wiersze nadal działają. Może to nie wpływać na to, co widzi przeglądarka, ale jeśli te wiersze wykonują jakiekolwiek inne przetwarzanie, może to być naprawdę mylące.
Joshua Frank
3
Nie mogę myśleć, ale że formularze internetowe są zepsute przez projekt. Co bardziej obniża wydajność, wywołuje Response.End () lub pozwala stronie ładować wszystko, a następnie tłumi odpowiedź? Nie widzę, gdzie Response.End () jest „bardziej” szkodliwy. Ponadto Microsoft traktuje „ThreadAbortedException” jako normalne zdarzenie, jak wynika z tego kodu: referenceource.microsoft.com/#System.Web/UI/Page.cs,4875 Jedną z rzeczy, które są przeciwko Response.End () jest to, że może się nie powieść przerwanie odpowiedzi, co może czasami powodować wyświetlenie odpowiedzi.
Ghasan
99

To pytanie pojawia się u góry wszystkich wyszukiwań w Google, aby uzyskać informacje o odpowiedzi. Tak więc w przypadku innych wyszukiwań, takich jak ja, które chcą opublikować CSV / XML / PDF itp. W odpowiedzi na zdarzenie bez renderowania całej strony ASPX, tak to robię . (przesłanianie metod renderowania jest zbyt skomplikowane jak na tak proste zadanie 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()
Jay Zelos
źródło
1
Nie powinieneś używać do tego strony APSX. To dużo zmarnowanego wysiłku. Powinieneś używać ASMX lub usługi sieciowej, wszystko oprócz strony ASPX.
mattmanser
19
To wydaje się być odpowiedzią na najłatwiejsze wdrożenie. Kluczem jest Response.SuppressContent = True.
Chris Weber
3
@mattmanser - Nie zawsze jest łatwo / najlepiej / wskazane mieć osobną stronę dla różnych reprezentacji tego samego zasobu. Pomyśl o REST itp. Jeśli klient zasugeruje, że chce csv, xml przez nagłówek lub parametr, ta metoda byłaby z pewnością najlepsza, jednocześnie zapewniając obsługę HTML za pośrednictwem normalnych funkcji renderowania asp.net.
Chris Weber
1
To mi nie zadziałało. Miałem stronę, która działała z Response.End (), ale używając wszelkiego rodzaju kombinacji Response.Close (), Response.Flush (). HttpContext.Current.ApplicationInstance.CompleteRequest () i różne inne rzeczy nie działały, gdybym miał filtr GzipStream w odpowiedzi. Wydawało się, że dzieje się tak, że strona wciąż jest wyświetlana wraz z moim plikiem. W końcu przesłoniłem funkcję Render () (żeby była pusta) i to dla mnie rozwiązało.
Aerik,
1
CompleteRequest pomija części potoku aplikacji, ale nadal będzie przebiegał przez pozostałą część procesu renderowania strony, co nie jest natychmiastowym zatrzymaniem, jak odpowiedź. Koniec, jest bardziej wdzięczne. Istnieje więcej szczegółowych wyjaśnień, dlaczego w innych odpowiedziach na tej stronie.
Jay Zelos,
11

Na pytanie „Nadal nie znam różnicy między Response.Close a CompleteRequest ()” powiedziałbym:

Wolisz CompleteRequest (), nie używaj Response.Close ().

Zapoznaj się z poniższym artykułem, aby uzyskać dobrze wykonane podsumowanie tej sprawy.

Należy pamiętać, że nawet po wywołaniu CompleteRequest () do tekstu wyjściowego odpowiedzi zostanie dołączony jakiś tekst (np. Ponownie odczytany z kodu ASPX). Można temu zapobiec, zastępując metody Render i RaisePostBackEvent zgodnie z opisem w poniższym artykule .

BTW: Zgadzam się z zapobieganiem używaniu Response.End (), szczególnie przy zapisywaniu danych do strumienia http w celu emulacji pobierania plików. W przeszłości używaliśmy Response.End (), dopóki nasz plik dziennika nie zapełnił się wyjątkami ThreadAbortException.

Jan Šotola
źródło
Interesuje mnie przesłanianie renderowania, tak jak to opisujesz, ale link do „następnego artykułu” jest martwy. Być może możesz zaktualizować swój wpis?
paqogomez
Przepraszam za spóźnioną odpowiedź. Nie pamiętam dokładnie, co było w tym artykule. Znalazłem go jednak na stronie webarchive.org: web.archive.org/web/20101224113858/http://www.c6software.com/…
Jan Šotola
9

Nie zgadzam się ze stwierdzeniem „ Odpowiedź. Koniec jest szkodliwy ”. To zdecydowanie nie jest szkodliwe. Response.End robi to, co mówi; kończy wykonywanie strony. Używanie reflektora do sprawdzenia, w jaki sposób został zaimplementowany, należy traktować jedynie jako pouczające.


Moje 2-procentowe zalecenie
UNIKAJ używania Response.End()jako przepływu kontrolnego.
DO użycie Response.End()jeśli trzeba zatrzymać wykonanie żądań i mieć świadomość, że (zazwyczaj) * kod nie wykona przeszłości tego miejsca.


* Response.End()i ThreadAbortException s.

Response.End() zgłasza wyjątek ThreadAbortException w ramach jego bieżącej implementacji (jak zauważył OP).

ThreadAbortException to specjalny wyjątek, który można przechwycić, ale zostanie on automatycznie zgłoszony ponownie na końcu bloku catch.

Aby zobaczyć, jak napisać kod, który musi radzić sobie z Wyjątkami ThreadAbort, zobacz odpowiedź @ Mehrdada na SO. Jak mogę wykryć wyjątek typu Threadabort w ostatnim bloku, w którym odwołuje się on do metody RuntimeHelpers.ExecuteCodeWithGuaranteedCleanup i regionów ograniczonego wykonania


Wspomniany artykuł Ricka Strahla jest pouczający i koniecznie przeczytaj komentarze. Zauważ, że problem Strahla był specyficzny. Chciał przekazać dane klientowi (obraz), a następnie przetworzyć aktualizację bazy danych śledzenia trafień, co nie spowolniło wyświetlania obrazu, co sprawiło, że miał problem z zrobieniem czegoś po wywołaniu odpowiedzi.

Robert Paulson
źródło
Widzieliśmy ten post stackoverflow.com/questions/16731745/... sugerujący użycie Response.SuppressContent = True HttpContext.Current.ApplicationInstance.CompleteRequest () zamiast Response.End ()
jazzBox
3

Nigdy nie rozważałem użycia Response.End () do sterowania przebiegiem programu.

Jednak Response.End () może być przydatny na przykład podczas udostępniania plików użytkownikowi.

Zapisałeś plik do odpowiedzi i nie chcesz, aby do odpowiedzi dodawano nic innego, ponieważ może to spowodować uszkodzenie pliku.

Ciasto rybne
źródło
1
Rozumiem, że interfejs API musi powiedzieć „odpowiedź jest kompletna”. Ale Response.End () również przerywa wątek. To jest sedno pytania. Kiedy warto połączyć te dwie rzeczy?
Cheeso,
2

Używałem Response.End () zarówno w .NET, jak i Classic ASP, aby wcześniej skutecznie wymusić zakończenie. Na przykład używam go, gdy istnieje pewna liczba prób logowania. Lub gdy uzyskuje się dostęp do bezpiecznej strony z nieuwierzytelnionego logowania (szorstki przykład):

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

Podczas udostępniania plików użytkownikowi używałbym koloru, koniec może powodować problemy.

Tim Meers
źródło
Pamiętaj, że Flush () to nie „to koniec”. Po prostu „spłucz wszystko do tej pory”. Powodem, dla którego możesz chcieć „to koniec”, jest poinformowanie klienta, że ​​ma całą zawartość, podczas gdy serwer może iść i robić inne rzeczy - aktualizować plik dziennika, sprawdzać licznik bazy danych lub cokolwiek innego. Jeśli zadzwonisz do Response.Flush, a następnie wykonasz jedną z tych czynności, klient może nadal czekać na więcej. Jeśli wywołasz Response.End (), kontrola wyskoczy, a DB nie otrzyma zapytań itp.
Cheeso
Alternatywnie można użyć przesłonięcia Response.Redirect („....”, true), gdzie bool to „endResponse: Wskazuje, czy bieżące wykonanie strony powinno się zakończyć”
Robert Paulson
Zawsze lepiej jest używać struktury Forms Authentication do ochrony stron, które mają być zabezpieczone przy użyciu poświadczeń logowania.
Robert Paulson,
3
Właściwie, w celu poprawienia siebie, uważam, że domyślnie Response.Redirect i Server.Transfer mają wywoływać Response.End wewnętrznie, chyba że wywołasz przesłonięcie i przekażesz „false”. Sposób, w jaki napisany jest kod Response.End nigdy nie jest wywoływany ,
Robert Paulson,
1
Response.end działa znacznie inaczej w .net niż w klasycznej ASP. W .net powoduje wyjątek dotyczący wątku, który może być dość nieprzyjemny.
Andy
2

Użyłem tylko Response.End () jako mechanizmu testowania / debugowania

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

Sądząc z tego, co napisałeś w zakresie badań, powiedziałbym, że byłby to zły projekt, gdyby wymagał odpowiedzi.

Nathan Koop
źródło
0

W klasycznym asp miałem TTFB (czas do pierwszego bajtu) wynoszący od 3 do 10 sekund w przypadku niektórych wywołań ajax, znacznie większy niż TTFB na zwykłych stronach z większą liczbą wywołań SQL.

Zwrócony ajax był fragmentem HTML, który ma zostać wstrzyknięty na stronę.

TTFB był o kilka sekund dłuższy niż czas renderowania.

Jeśli dodałem odpowiedź. Koniec po renderowaniu, TTFB został znacznie zmniejszony.

Mógłbym uzyskać ten sam efekt, emitując „</body> </html>”, ale to prawdopodobnie nie działa, gdy wypisujesz json lub xml; tutaj potrzebna jest odpowiedź. koniec.

Leif Neland
źródło
Wiem, że to stara odpowiedź, ale z drugiej strony klasyczny boleń jest stary.
Przydało