Response.End () est-il considéré comme dangereux?

198

Cet article de la base de Response.End()connaissances indique que ASP.NET abandonne un thread.

Le réflecteur montre que cela ressemble à ceci:

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

Cela me semble assez dur. Comme le dit l'article de la base de connaissances, aucun code de l'application suivante Response.End()ne sera exécuté, ce qui viole le principe du moindre étonnement. C'est presque comme Application.Exit()dans une application WinForms. L'exception d'annulation de thread provoquée par Response.End()n'est pas capturable, donc entourer le code dans un try... finallyne sera pas satisfaisant.

Je me demande si je devrais toujours éviter Response.End().

Quelqu'un peut-il suggérer, quand dois-je utiliser Response.End(), quand Response.Close()et quand HttpContext.Current.ApplicationInstance.CompleteRequest()?

réf: entrée de blog de Rick Strahl .


Sur la base des informations que j'ai reçues, ma réponse est oui, Response.Endest nuisible , mais elle est utile dans certains cas limités.

  • utiliser Response.End()comme un jet inaccessible, pour terminer immédiatement le HttpResponsedans des conditions exceptionnelles. Peut également être utile lors du débogage. Évitez Response.End()de compléter les réponses de routine .
  • utiliser Response.Close()pour fermer immédiatement la connexion avec le client. Selon cet article de blog MSDN , cette méthode n'est pas destinée au traitement normal des requêtes HTTP. Il est très peu probable que vous ayez une bonne raison d'appeler cette méthode.
  • utiliser CompleteRequest()pour mettre fin à une demande normale. CompleteRequestentraîne le pipeline ASP.NET à passer à l' EndRequestévénement, une fois l' HttpApplicationévénement en cours terminé. Donc, si vous appelez CompleteRequest, puis écrivez quelque chose de plus dans la réponse, l'écriture sera envoyée au client.

Edit - 13 avril 2011

Plus de clarté est disponible ici:
- Article utile sur le blog MSDN
- Analyse utile par Jon Reid

Cheeso
la source
2
aucune idée de ce qui a changé depuis cette réponse, mais je saisis Response.End ThreadAbortExceptiontrès bien.
Maslow
1
Gardez à l' esprit que Response.Redirectet à la Server.Transferfois appel Response.Endet devrait également être évité.
Owen Blacker

Réponses:

66

Si vous aviez utilisé un enregistreur d'exceptions sur votre application, il sera édulcoré avec les ThreadAbortExceptions de ces Response.End()appels bénins . Je pense que c'est la manière de Microsoft de dire "Arrête!".

Je n'utiliserais que Response.End()s'il y avait une condition exceptionnelle et qu'aucune autre action n'était possible. Peut-être qu'alors, l'enregistrement de cette exception pourrait en fait indiquer un avertissement.

spoulson
la source
107

TL; DR

Au départ, j'avais recommandé que vous remplaciez simplement tous vos appels à [Response.End] par des appels à [...] CompleteRequest (), mais si vous voulez éviter le traitement de publication et le rendu html, vous devrez ajouter [.. .] remplace également.

Jon Reid , "Analyse finale"


Selon MSDN, Jon Reid et Alain Renon:

Performances ASP.NET - Gestion des exceptions - Écrire du code qui évite les exceptions

Les méthodes Server.Transfer, Response.Redirect, Response.End déclenchent toutes des exceptions. Chacune de ces méthodes appelle en interne Response.End. L'appel à Response.End, à son tour, provoque une exception ThreadAbortException .

Solution ThreadAbortException

HttpApplication.CompleteRequest () définit une variable qui oblige le thread à ignorer la plupart des événements dans le pipeline d'événements HttpApplication [-] non pas la chaîne d'événements Page, mais la chaîne d'événements Application.

...

créez une variable de niveau classe qui signale si la page doit se terminer, puis vérifiez la variable avant de traiter vos événements ou de rendre votre page. [...] Je recommanderais simplement de remplacer les méthodes RaisePostBackEvent et Render

Response.End et Response.Close ne sont pas utilisés dans le traitement normal des demandes lorsque les performances sont importantes. Response.End est un moyen pratique et compliqué de terminer le traitement des demandes avec une pénalité de performance associée. Response.Close est pour l'arrêt immédiat de la réponse HTTP au niveau IIS / socket et provoque des problèmes avec des choses comme KeepAlive.

La méthode recommandée pour mettre fin à une demande ASP.NET est HttpApplication.CompleteRequest. Gardez à l'esprit que le rendu ASP.NET devra être ignoré manuellement car HttpApplication.CompleteRequest ignore le reste du pipeline d'application IIS / ASP.NET, pas le pipeline de page ASP.NET (qui est une étape du pipeline d'application).


Code

Copyright © 2001-2007, C6 Software, Inc du mieux que je puisse dire.


Référence

HttpApplication.CompleteRequest

ASP.NET contourne tous les événements et le filtrage dans la chaîne d'exécution du pipeline HTTP et exécute directement l'événement EndRequest.

Response.End

Cette méthode est fournie uniquement pour la compatibilité avec ASP, c'est-à-dire pour la compatibilité avec la technologie de programmation Web basée sur COM qui a précédé ASP.NET.preceded ASP.NET. [Italiques ajoutés]

Réponse.Fermer

Cette méthode met fin à la connexion au client de manière abrupte et n'est pas destinée au traitement normal des requêtes HTTP . [Italiques ajoutés]

user423430
la source
4
> Gardez à l'esprit que le rendu ASP.NET devra être ignoré manuellement car HttpApplication.CompleteRequest ignore le reste du pipeline d'application IIS / ASP.NET, pas le pipeline de page ASP.NET (qui est une étape du pipeline d'application). Et comment accomplissez-vous cela?
PilotBob
1
Voir le lien vers le code où Jon Reid a montré comment définir un indicateur et remplacer les méthodes RaisePostBackEvent et Render de la page pour ignorer l'implémentation normale lorsque vous le souhaitez. (Vous auriez probablement le faire dans une classe de base toutes les pages de votre application doivent hériter de.) Web.archive.org/web/20101224113858/http://www.c6software.com/...
user423430
4
Juste pour reformuler: HttpApplication.CompleteRequest ne termine pas la réponse comme le fait Response.End.
Glen Little
2
HttpApplication.CompleteRequest n'arrête pas non plus le flux de code, donc les lignes suivantes continuent de fonctionner. Cela peut ne pas affecter ce que le navigateur voit, mais si ces lignes effectuent un autre traitement, cela peut être vraiment déroutant.
Joshua Frank
3
Je ne peux pas penser mais que les formulaires Web sont cassés par conception. Quoi de plus dégradant les performances, appeler Response.End () ou laisser la page tout charger, puis supprimer la réponse? Je ne vois pas où Response.End () est "plus" nuisible ici. De plus, Microsoft traite `ThreadAbortedException 'comme un événement normal comme en témoigne ce code: referencesource.microsoft.com/#System.Web/UI/Page.cs,4875 Une chose qui est contre Response.End () est qu'il pourrait échouer abandonner la réponse, ce qui peut occasionnellement entraîner l'affichage de la réponse.
Ghasan
99

Cette question apparaît en haut de toutes les recherches Google pour obtenir des informations sur response.end donc pour d'autres recherches comme moi qui souhaitent publier CSV / XML / PDF, etc. en réponse à un événement sans afficher l'intégralité de la page ASPX, voici comment je le fais . (remplacer les méthodes de rendu est trop complexe pour une tâche aussi simple 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
la source
1
Vous ne devriez pas utiliser une page APSX pour ce faire. C'est beaucoup d'efforts gaspillés. Vous êtes censé utiliser un ASMX ou un service Web, tout sauf une page ASPX.
mattmanser
19
Cela semble être la réponse avec la mise en œuvre la plus simple. La clé est Response.SuppressContent = True.
Chris Weber
3
@mattmanser - Il n'est pas toujours facile / préférable / conseillé d'avoir une page séparée pour une représentation différente de la même ressource. Pensez à REST, etc. Si le client indique qu'il veut csv, xml via un en-tête ou param, cette méthode serait certainement la meilleure, tout en fournissant un support html via les fonctionnalités de rendu normales d'asp.net.
Chris Weber
1
Ça n'a pas marché pour moi. J'avais une page qui fonctionnait avec Response.End (), mais en utilisant toutes sortes de combinaisons de Response.Close (), Response.Flush (). HttpContext.Current.ApplicationInstance.CompleteRequest () et diverses autres choses ne fonctionnaient pas si j'avais un filtre GzipStream sur la réponse. Ce qui semblait se produire, c'est que la page était toujours en cours de sortie avec mon fichier. J'ai finalement annulé la fonction Render () (pour être vide) et cela l'a résolu pour moi.
Aerik
1
CompleteRequest ignore certaines parties du pipeline d'application, mais continuera d'exécuter le reste du processus de rendu de page, ce n'est pas un arrêt immédiat comme response.end, c'est plus gracieux. Il y a des explications plus détaillées sur pourquoi dans d'autres réponses sur cette page.
Jay Zelos
11

Sur la question "Je ne connais toujours pas la différence entre Response.Close et CompleteRequest ()", je dirais:

Préférez CompleteRequest (), n'utilisez pas Response.Close ().

Voir l' article suivant pour un résumé bien fait de ce cas.

N'oubliez pas que même après avoir appelé CompleteRequest (), du texte (par exemple redndered à partir du code ASPX) serait ajouté au flux de sortie de la réponse. Vous pouvez l'empêcher en remplaçant les méthodes Render et RaisePostBackEvent comme décrit dans l' article suivant .

BTW: Je suis d'accord pour empêcher l'utilisation de Response.End (), en particulier lors de l'écriture de données dans le flux http pour émuler le téléchargement de fichiers. Nous avons utilisé Response.End () dans le passé jusqu'à ce que notre fichier journal soit rempli de ThreadAbortExceptions.

Jan Šotola
la source
Je suis intéressé à remplacer Render comme vous le décrivez, mais le lien vers «l'article suivant» est mort. Peut-être pouvez-vous mettre à jour votre entrée?
paqogomez
Désolé pour une réponse tardive. Je ne me souviens pas exactement de ce qu'il y avait dans cet article. Cependant, je l'ai trouvé sur le site webarchive.org: web.archive.org/web/20101224113858/http://www.c6software.com/…
Jan Šotola
9

Je ne suis pas d'accord avec l'énoncé " Response.End is nocive ". Ce n'est certainement pas nocif. Response.End fait ce qu'il dit; il termine l'exécution de la page. Utiliser le réflecteur pour voir comment il a été mis en œuvre ne doit être considéré que comme instructif.


Ma 2cent Recommandation
ÉVITER en utilisant Response.End()comme flux de contrôle.
À utiliser Response.End()si vous devez arrêter l'exécution de la demande et sachez que (généralement) * aucun code ne sera exécuté après ce point.


* Response.End() et ThreadAbortException s.

Response.End() lève une exception ThreadAbortException dans le cadre de son implémentation actuelle (comme indiqué par OP).

ThreadAbortException est une exception spéciale qui peut être interceptée, mais elle sera automatiquement levée à nouveau à la fin du bloc catch.

Pour voir comment écrire du code qui doit gérer les ThreadAbortExceptions, voir la réponse de @ Mehrdad à SO Comment puis-je détecter une threadabortexception dans un bloc finally où il fait référence à RuntimeHelpers.ExecuteCodeWithGuaranteedCleanup et aux régions d'exécution contraintes


L' article de Rick Strahl mentionné est instructif et assurez-vous de lire également les commentaires. Notez que le problème de Strahl était spécifique. Il voulait transmettre les données au client (une image), puis traiter la mise à jour de la base de données de suivi des hits qui ne ralentissait pas la diffusion de l'image, ce qui lui posait le problème de faire quelque chose après que Response.End ait été appelé.

Robert Paulson
la source
Nous avons vu ce post stackoverflow.com/questions/16731745/… suggérant d'utiliser Response.SuppressContent = True HttpContext.Current.ApplicationInstance.CompleteRequest () au lieu de Response.End ()
jazzBox
3

Je n'ai jamais envisagé d'utiliser Response.End () pour contrôler le flux du programme.

Cependant Response.End () peut être utile par exemple lors du service de fichiers à un utilisateur.

Vous avez écrit le fichier dans la réponse et vous ne voulez rien ajouter à la réponse car cela pourrait corrompre votre fichier.

Gateau au poisson
la source
1
Je comprends la nécessité pour une API de dire "la réponse est complète". Mais Response.End () fait également un abandon de thread. C'est le nœud de la question. Quand est-ce une bonne idée de coupler ces deux choses?
Cheeso
2

J'ai utilisé Response.End () dans .NET et ASP classique pour terminer les choses avec force auparavant. Par exemple, je l'utilise lorsqu'il y a un nombre certian de tentatives de connexion. Ou lorsqu'une page sécurisée est accédée à partir d'une connexion non authentifiée (exemple grossier):

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

Lors de la diffusion de fichiers à un utilisateur que j'utiliserais un vidage, la fin peut provoquer des problèmes.

Tim Meers
la source
Gardez à l'esprit que Flush () n'est pas "c'est la fin". C'est juste "rincer tout jusqu'à présent". La raison pour laquelle vous voudrez peut-être "c'est la fin" est de laisser le client être conscient qu'il a tout le contenu, tandis que le serveur peut aller faire d'autres choses - mettre à jour un fichier journal, interroger un compteur de base de données, ou autre chose. Si vous appelez Response.Flush et puis effectuez l'une de ces choses, le client peut continuer d'attendre plus. Si vous appelez Response.End (), alors le contrôle saute et la base de données ne reçoit pas de requêtes, etc.
Cheeso
Vous pouvez également utiliser la substitution Response.Redirect ("....", true) où le booléen est 'endResponse: indique si l'exécution en cours de la page doit se terminer "
Robert Paulson
Il est toujours préférable d'utiliser le cadre d'authentification par formulaire pour protéger les pages qui sont censées être sécurisées par les informations d'identification de connexion.
Robert Paulson
3
En fait, pour me corriger, je pense que la valeur par défaut de Response.Redirect et Server.Transfer est d'appeler Response.End en interne, sauf si vous appelez la substitution et passez "false". La façon dont votre code est écrit, Response.End n'est jamais appelé ,
Robert Paulson
1
Response.end fonctionne beaucoup différemment dans .net que dans ASP classique. Dans .net, cela provoque une exception de threadabort, qui peut être assez désagréable.
Andy
2

Je n'ai utilisé que Response.End () comme mécanisme de test / débogage

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

À en juger par ce que vous avez publié en termes de recherche, je dirais que ce serait une mauvaise conception si elle nécessitait une réponse.

Nathan Koop
la source
0

Sur asp classique, j'avais un TTFB (Time To First Byte) de 3 à 10 secondes sur certains appels ajax, beaucoup plus grand que le TTFB sur les pages régulières avec beaucoup plus d'appels SQL.

L'ajax retourné était un segment de HTML à injecter dans la page.

Le TTFB était plusieurs secondes plus long que le temps de rendu.

Si j'ai ajouté un response.end après le rendu, le TTFB a été considérablement réduit.

Je pourrais obtenir le même effet en émettant un "</body> </html>", mais cela ne fonctionne probablement pas lors de la sortie de json ou xml; ici response.end est nécessaire.

Leif Neland
la source
Je sais que c'est une vieille réponse, mais là encore, l'asp classique est vieux. Je l'ai trouvé utile ;-)
Leif Neland