Wird Response.End () als schädlich angesehen?

198

Dieser KB-Artikel besagt, dass ASP.NET Response.End()einen Thread abbricht.

Reflektor zeigt, dass es so aussieht:

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

Das scheint mir ziemlich hart zu sein. Wie der KB-Artikel sagt, wird kein Code in der folgenden App Response.End()ausgeführt, was gegen das Prinzip des geringsten Erstaunens verstößt. Es ist fast wie Application.Exit()in einer WinForms-App. Die durch verursachte Thread-Abbruch-Ausnahme Response.End()ist nicht abfangbar, sodass das Umgeben des Codes in einem try... finallynicht zufriedenstellend ist.

Ich frage mich, ob ich es immer vermeiden sollte Response.End().

Kann jemand vorschlagen, wann ich Response.End()wann Response.Close()und wann verwenden soll HttpContext.Current.ApplicationInstance.CompleteRequest()?

Ref: Rick Strahls Blogeintrag .


Basierend auf den Eingaben, die ich erhalten habe, lautet meine Antwort: Ja, Response.Endist schädlich , aber in einigen begrenzten Fällen nützlich.

  • Verwenden Sie Response.End()als nicht fangbarer Wurf, um die HttpResponsein außergewöhnlichen Bedingungen sofort zu beenden . Kann auch beim Debuggen hilfreich sein. Vermeiden Sie Response.End()routinemäßige Antworten .
  • Verwenden Sie Response.Close()diese Option, um die Verbindung zum Client sofort zu schließen. Gemäß diesem MSDN-Blogbeitrag ist diese Methode nicht für die normale Verarbeitung von HTTP-Anforderungen vorgesehen. Es ist höchst unwahrscheinlich, dass Sie einen guten Grund haben, diese Methode aufzurufen.
  • Verwenden Sie CompleteRequest()diese Option, um eine normale Anforderung zu beenden. CompleteRequestbewirkt, dass die ASP.NET-Pipeline EndRequestnach Abschluss des aktuellen HttpApplicationEreignisses zum Ereignis springt . Wenn Sie also anrufen CompleteRequestund dann etwas mehr in die Antwort schreiben, wird der Schreibvorgang an den Client gesendet.

Bearbeiten - 13. April 2011

Weitere Klarheit finden Sie hier:
- Nützlicher Beitrag im MSDN-Blog
- Nützliche Analyse von Jon Reid

Cheeso
quelle
2
Keine Ahnung, was sich seit dieser Antwort geändert hat, aber ich verstehe das Response.End ThreadAbortExceptionganz gut.
Maslow
1
Denken Sie auch daran Response.Redirectund Server.Transferbeide rufen an Response.Endund sollten auch vermieden werden.
Owen Blacker

Antworten:

66

Wenn Sie in Ihrer App einen Ausnahmeprotokollierer verwendet haben, wird dieser mit dem ThreadAbortExceptions dieser harmlosen Response.End()Anrufe verwässert . Ich denke, dies ist die Art von Microsoft, "Knock it off!" Zu sagen.

Ich würde es nur verwenden, Response.End()wenn es einen außergewöhnlichen Zustand gäbe und keine andere Aktion möglich wäre. Vielleicht weist das Protokollieren dieser Ausnahme dann tatsächlich auf eine Warnung hin.

Spoulson
quelle
107

TL; DR

Anfangs hatte ich empfohlen, dass Sie einfach alle Ihre Aufrufe von [Response.End] durch [...] CompleteRequest () -Aufrufe ersetzen sollten. Wenn Sie jedoch die Postback-Verarbeitung und das HTML-Rendering vermeiden möchten, müssen Sie [.. .] überschreibt auch.

Jon Reid , "Endgültige Analyse"


Per MSDN, Jon Reid und Alain Renon:

ASP.NET-Leistung - Ausnahmeverwaltung - Schreiben Sie Code, der Ausnahmen vermeidet

Die Methoden Server.Transfer, Response.Redirect und Response.End lösen alle Ausnahmen aus. Jede dieser Methoden ruft intern Response.End auf. Der Aufruf von Response.End verursacht wiederum eine ThreadAbortException-Ausnahme .

ThreadAbortException-Lösung

HttpApplication.CompleteRequest () legt eine Variable fest, die bewirkt, dass der Thread die meisten Ereignisse in der HttpApplication-Ereignispipeline [-] überspringt, nicht die Page-Ereigniskette, sondern die Application-Ereigniskette.

...

Erstellen Sie eine Variable auf Klassenebene, die angibt, ob die Seite beendet werden soll, und überprüfen Sie die Variable, bevor Sie Ihre Ereignisse verarbeiten oder Ihre Seite rendern. [...] Ich würde empfehlen, nur die Methoden RaisePostBackEvent und Render zu überschreiben

Response.End und Response.Close werden bei der normalen Anforderungsverarbeitung nicht verwendet, wenn die Leistung wichtig ist. Response.End ist ein praktisches und hartnäckiges Mittel zum Beenden der Anforderungsverarbeitung mit einer damit verbundenen Leistungseinbuße. Response.Close dient zum sofortigen Beenden der HTTP-Antwort auf IIS- / Socket-Ebene und verursacht Probleme mit Dingen wie KeepAlive.

Die empfohlene Methode zum Beenden einer ASP.NET-Anforderung ist HttpApplication.CompleteRequest. Beachten Sie, dass das ASP.NET-Rendering manuell übersprungen werden muss, da HttpApplication.CompleteRequest den Rest der IIS / ASP.NET-Anwendungspipeline überspringt, nicht die ASP.NET-Seitenpipeline (die eine Stufe in der App-Pipeline darstellt).


Code

Copyright © 2001-2007, C6 Software, Inc, so gut ich es beurteilen konnte.


Referenz

HttpApplication.CompleteRequest

Bewirkt, dass ASP.NET alle Ereignisse und Filter in der Ausführungskette der HTTP-Pipeline umgeht und das EndRequest-Ereignis direkt ausführt.

Response.End

Diese Methode wird nur aus Gründen der Kompatibilität mit ASP bereitgestellt. Dies dient der Kompatibilität mit der COM-basierten Webprogrammierungstechnologie, die vor ASP.NET.preceded ASP.NET stand. [Betonung hinzugefügt]

Antwort. Schließen

Diese Methode beendet die Verbindung zum Client abrupt und ist nicht für die normale Verarbeitung von HTTP-Anforderungen vorgesehen . [Betonung hinzugefügt]

user423430
quelle
4
> Beachten Sie, dass das ASP.NET-Rendering manuell übersprungen werden muss, da HttpApplication.CompleteRequest den Rest der IIS / ASP.NET-Anwendungspipeline überspringt, nicht die ASP.NET-Seitenpipeline (die eine Stufe in der App-Pipeline darstellt). Und wie schaffen Sie das?
PilotBob
1
Siehe den Link zu dem Code, in dem Jon Reid demonstrierte, wie ein Flag gesetzt und die RaisePostBackEvent- und Render-Methoden der Seite überschrieben werden, um die normale Implementierung bei Bedarf zu überspringen. (Sie würden dies wahrscheinlich in einer Basisklasse tun, von der alle Seiten Ihrer App erben sollten.) Web.archive.org/web/20101224113858/http://www.c6software.com/…
user423430
4
Nur um es noch einmal zu wiederholen: HttpApplication.CompleteRequest beendet die Antwort nicht wie Response.End.
Glen Little
2
HttpApplication.CompleteRequest stoppt auch nicht den Codefluss, sodass nachfolgende Zeilen weiter ausgeführt werden. Das hat möglicherweise keinen Einfluss darauf, was der Browser sieht, aber wenn diese Zeilen eine andere Verarbeitung ausführen, kann dies sehr verwirrend sein.
Joshua Frank
3
Ich kann nicht denken, dass Web Forms vom Design her kaputt ist. Was bedeutet mehr Leistungseinbußen, wenn Sie Response.End () aufrufen oder die Seite alles laden lassen und dann die Antwort unterdrücken? Ich kann nicht sehen, wo Response.End () hier "schädlicher" ist. Darüber hinaus behandelt Microsoft "ThreadAbortedException" als normales Ereignis, wie aus diesem Code hervorgeht: referencesource.microsoft.com/#System.Web/UI/Page.cs,4875 Eine Sache, die gegen Response.End () spricht, ist, dass es möglicherweise fehlschlägt Abbrechen der Antwort, was gelegentlich dazu führen kann, dass die Antwort angezeigt wird.
Ghasan
99

Diese Frage wird oben in allen Google-Suchanfragen nach Informationen zu response.end angezeigt. Für andere Suchanfragen wie mich, die CSV / XML / PDF usw. als Antwort auf ein Ereignis veröffentlichen möchten, ohne die gesamte ASPX-Seite zu rendern, gehe ich folgendermaßen vor . (Das Überschreiben der Rendermethoden ist für eine so einfache Aufgabe IMO zu komplex.)

// 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
quelle
1
Sie sollten dazu keine APSX-Seite verwenden. Es ist viel verschwendete Mühe. Sie sollten einen ASMX- oder Webdienst verwenden, alles andere als eine ASPX-Seite.
Mattmanser
19
Dies scheint die Antwort mit der einfachsten Implementierung zu sein. Der Schlüssel ist Response.SuppressContent = True.
Chris Weber
3
@mattmanser - Es ist nicht immer einfach / am besten / ratsam, eine separate Seite für die unterschiedliche Darstellung derselben Ressource zu haben. Denken Sie an REST usw. Wenn der Client angibt, dass er CSV, XML über einen Header oder einen Parameter möchte, ist diese Methode sicherlich die beste und bietet dennoch HTML-Unterstützung über die normalen Rendering-Funktionen von asp.net.
Chris Weber
1
Das hat bei mir nicht funktioniert. Ich hatte eine Seite, die mit Response.End () funktionierte, aber alle Arten von Kombinationen von Response.Close (), Response.Flush () verwendete. HttpContext.Current.ApplicationInstance.CompleteRequest () und verschiedene andere Dinge funktionierten nicht, wenn ich einen GzipStream-Filter für die Antwort hatte. Was zu passieren schien, war, dass die Seite immer noch zusammen mit meiner Datei ausgegeben wurde. Ich habe endlich die Render () -Funktion überschrieben (um leer zu sein) und das hat es für mich gelöst.
Aerik
1
CompleteRequest überspringt Teile der Anwendungspipeline, durchläuft jedoch den Rest des Seitenrenderprozesses. Dies ist kein sofortiger Stopp wie response.end, sondern eleganter. In anderen Antworten auf dieser Seite finden Sie ausführlichere Erklärungen dazu, warum.
Jay Zelos
11

Zu der Frage "Ich kenne den Unterschied zwischen Response.Close und CompleteRequest () immer noch nicht" würde ich sagen:

Bevorzugen Sie CompleteRequest (), verwenden Sie nicht Response.Close ().

Im folgenden Artikel finden Sie eine ausführliche Zusammenfassung dieses Falls.

Beachten Sie, dass auch nach dem Aufruf von CompleteRequest () Text (z. B. aus ASPX-Code abgeleitet) an den Antwortausgabestream angehängt wird. Sie können dies verhindern, indem Sie die Methoden Render und RaisePostBackEvent überschreiben, wie im folgenden Artikel beschrieben .

Übrigens: Ich bin damit einverstanden, die Verwendung von Response.End () zu verhindern, insbesondere wenn Daten in den http-Stream geschrieben werden, um den Dateidownload zu emulieren. Wir haben in der Vergangenheit Response.End () verwendet, bis unsere Protokolldatei voller ThreadAbortExceptions war.

Jan Šotola
quelle
Ich bin daran interessiert, Render zu überschreiben, wie Sie beschreiben, aber der Link zum "folgenden Artikel" ist tot. Vielleicht können Sie Ihren Eintrag aktualisieren?
Paqogomez
Entschuldigung für eine späte Antwort. Ich erinnere mich nicht genau, was in diesem Artikel war. Ich habe es jedoch auf webarchive.org gefunden: web.archive.org/web/20101224113858/http://www.c6software.com/…
Jan Šotola
9

Ich bin mit der Aussage " Response.End is schädlich " nicht einverstanden . Es ist definitiv nicht schädlich. Response.End macht, was es sagt; Es beendet die Ausführung der Seite. Die Verwendung eines Reflektors, um zu sehen, wie er implementiert wurde, sollte nur als lehrreich angesehen werden.


Meine 2-Cent-Empfehlung
VERMEIDEN die Verwendung Response.End()als Kontrollfluss. Verwenden Sie diese
Option , Response.End()wenn Sie die Ausführung von Anforderungen stoppen müssen und sich darüber im Klaren sind, dass (normalerweise) * kein Code über diesen Punkt hinaus ausgeführt wird.


* Response.End()und ThreadAbortException s.

Response.End() löst eine ThreadAbortException als Teil der aktuellen Implementierung aus (wie von OP angegeben).

ThreadAbortException ist eine spezielle Ausnahme, die abgefangen werden kann, aber am Ende des catch-Blocks automatisch erneut ausgelöst wird.

Informationen zum Schreiben von Code, der sich mit ThreadAbortExceptions befassen muss, finden Sie in der Antwort von @ Mehrdad auf SO. Wie kann ich eine Threadabortexception in einem finally-Block erkennen, in dem er auf die RuntimeHelpers.ExecuteCodeWithGuaranteedCleanup-Methode und eingeschränkte Ausführungsbereiche verweist ?


Der erwähnte Artikel von Rick Strahl ist lehrreich und lesen Sie auch die Kommentare. Beachten Sie, dass Strahls Problem spezifisch war. Er wollte die Daten an den Client weiterleiten (ein Bild) und dann die Aktualisierung der Hit-Tracking-Datenbank verarbeiten, die die Bereitstellung des Bildes nicht verlangsamte, was ihn zum Problem machte, etwas zu tun, nachdem Response.End aufgerufen worden war.

Robert Paulson
quelle
Wir haben gesehen, dass dieser Beitrag stackoverflow.com/questions/16731745/… die Verwendung von Response.SuppressContent = True HttpContext.Current.ApplicationInstance.CompleteRequest () anstelle von Response.End ()
jazzBox
3

Ich habe nie darüber nachgedacht, Response.End () zur Steuerung des Programmflusses zu verwenden.

Response.End () kann jedoch beispielsweise beim Bereitstellen von Dateien an einen Benutzer hilfreich sein.

Sie haben die Datei in die Antwort geschrieben und möchten nicht, dass der Antwort etwas anderes hinzugefügt wird, da dies Ihre Datei beschädigen kann.

Fischkuchen
quelle
1
Ich verstehe die Notwendigkeit einer API, um zu sagen, dass die Antwort vollständig ist. Response.End () führt jedoch auch einen Thread-Abbruch durch. Dies ist der Kern der Frage. Wann ist es eine gute Idee, diese beiden Dinge zu koppeln?
Cheeso
2

Ich habe Response.End () sowohl in .NET als auch in Classic ASP verwendet, um Dinge zuvor zwangsweise zu beenden. Zum Beispiel verwende ich es, wenn es eine bestimmte Anzahl von Anmeldeversuchen gibt. Oder wenn eine sichere Seite von einem nicht authentifizierten Login aus aufgerufen wird (grobes Beispiel):

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

Wenn ich einem Benutzer Dateien bereitstelle, würde ich einen Flush verwenden. Das Ende kann Probleme verursachen.

Tim Meers
quelle
Denken Sie daran, Flush () ist nicht "das ist das Ende". Es ist nur "alles bisher spülen". Der Grund, warum Sie möglicherweise ein "Dies ist das Ende" wünschen, besteht darin, dem Client mitzuteilen, dass er über den gesamten Inhalt verfügt, während der Server andere Aufgaben ausführen kann - Aktualisieren einer Protokolldatei, Abfragen eines Datenbankzählers oder was auch immer. Wenn Sie Response.Flush aufrufen und dann eines dieser Dinge tun, wartet der Client möglicherweise weiter auf weitere. Wenn Sie Response.End () aufrufen, springt die Steuerung heraus und die DB erhält keine Abfragen usw.
Cheeso
Sie können alternativ die Überschreibungsantwort.Redirect ("....", true) verwenden, wobei der Bool 'endResponse' lautet: Gibt an, ob die aktuelle Ausführung der Seite beendet werden soll "
Robert Paulson
Es ist immer besser, das Formularauthentifizierungsframework zu verwenden, um Seiten zu schützen, die durch Anmeldeinformationen geschützt werden sollen.
Robert Paulson
3
Um mich selbst zu korrigieren, glaube ich, dass die Standardeinstellung von Response.Redirect und Server.Transfer darin besteht, Response.End intern aufzurufen, es sei denn, Sie rufen die Überschreibung auf und übergeben 'false'. So wie Ihr Code geschrieben ist, wird Response.End niemals aufgerufen ,
Robert Paulson
1
Response.end funktioniert in .net ganz anders als in klassischem ASP. In .net verursacht es eine Threadabortexception, die ziemlich böse sein kann.
Andy
2

Ich habe nur Response.End () als Test- / Debugging-Mechanismus verwendet

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

Nach dem zu urteilen, was Sie in Bezug auf die Forschung gepostet haben, würde ich sagen, dass es ein schlechtes Design wäre, wenn es Response.End erfordern würde

Nathan Koop
quelle
0

Auf klassischem Asp hatte ich bei einigen Ajax-Aufrufen einen TTFB (Time To First Byte) von 3 bis 10 Sekunden, viel größer als der TTFB auf normalen Seiten mit viel mehr SQL-Aufrufen.

Der zurückgegebene Ajax war ein HTML-Segment, das in die Seite eingefügt werden sollte.

Der TTFB war einige Sekunden länger als die Renderzeit.

Wenn ich nach dem Rendern ein response.end hinzufügte, wurde der TTFB stark reduziert.

Ich könnte den gleichen Effekt erzielen, indem ich ein "</ body> </ html>" ausgeben würde, aber dies funktioniert wahrscheinlich nicht bei der Ausgabe von json oder xml. hier wird response.end benötigt.

Leif Neland
quelle
Ich weiß, dass dies eine alte Antwort ist, aber andererseits ist klassischer Asp alt. Ich fand es nützlich ;-)
Leif Neland