web-dev-qa-db-fra.com

Pourquoi Response.Redirect provoque une exception System.Threading.ThreadAbortException?

Lorsque j'utilise Response.Redirect (...) pour rediriger mon formulaire vers une nouvelle page, le message d'erreur suivant s'affiche:

Une exception de première chance du type 'System.Threading.ThreadAbortException' s'est produite dans mscorlib.dll
Une exception de type 'System.Threading.ThreadAbortException' s'est produite dans mscorlib.dll mais n'a pas été gérée dans le code utilisateur.

Si j'ai bien compris, l'erreur provient du fait que le serveur Web a abandonné le reste de la page sur laquelle le fichier response.redirect a été appelé.

Je sais que je peux ajouter un deuxième paramètre à Response.Redirect, appelé endResponse. Si je mets endResponse à True, j'obtiens toujours l'erreur mais si je la mets à False, je ne le fais pas. Je suis à peu près sûr que cela signifie que le serveur Web exécute le reste de la page que je redirige. Ce qui semble être inefficace pour le moins. Y a-t-il une meilleure manière de faire cela? Quelque chose d'autre que Response.Redirect ou existe-t-il un moyen de forcer l'ancienne page à cesser de charger sans obtenir un ThreadAbortException?

225
Ben Hoffman

Le modèle correct consiste à appeler la surcharge de redirect avec endResponse = false et à appeler le pipeline IIS pour lui indiquer qu'il doit passer directement à l'étape EndRequest une fois que vous avez renvoyé le contrôle:

Response.Redirect(url, false);
Context.ApplicationInstance.CompleteRequest();

Cet article de blog de Thomas Marquardt fournit des détails supplémentaires, y compris comment gérer le cas particulier de redirection dans un gestionnaire Application_Error.

325
Joel Fillmore

Il existe non une solution simple et élégante au problème Redirect dans ASP.Net WebForms. Vous pouvez choisir entre la solution sale et la solution fastidieuse

Dirty: Response.Redirect(url) envoie une redirection au navigateur, puis lance un ThreadAbortedException pour mettre fin au thread actuel. Donc, aucun code n'est exécuté après l'appel à Redirect (). Inconvénients: c'est une mauvaise pratique et des implications en termes de performances de tuer des threads comme celui-ci. De plus, ThreadAbortedExceptions apparaîtra dans la journalisation des exceptions.

fastidieux: La méthode recommandée consiste à appeler Response.Redirect(url, false) puis Context.ApplicationInstance.CompleteRequest() Cependant, l'exécution du code se poursuivra et le reste des gestionnaires d'événements du cycle de vie de la page sera toujours exécuté. (Par exemple, si vous effectuez la redirection dans Page_Load, non seulement le reste du gestionnaire sera exécuté, Page_PreRender, etc., sera également appelé - la page rendue ne sera tout simplement pas envoyée au navigateur. Vous pouvez éviter le traitement supplémentaire en Par exemple, définir un indicateur sur la page, puis laisser les gestionnaires d’événements ultérieurs vérifier cet indicateur avant d’effectuer un traitement.

(La documentation de CompleteRequest indique qu'elle " force ASP.NET à ignorer tous les événements et à filtrer dans la chaîne d'exécution du pipeline HTTP ". Cela peut facilement être mal compris: il ignore d'autres filtres et modules HTTP, mais pas d'autres événements dans le cycle de vie actuel de la page .)

Le problème plus profond est que WebForms manque d'un niveau d'abstraction. Lorsque vous êtes dans un gestionnaire d'événements, vous êtes déjà en train de créer une page à afficher. La redirection dans un gestionnaire d'événements est moche car vous arrêtez une page partiellement générée afin de générer une page différente. MVC n'a pas ce problème puisque le flux de contrôle est séparé des vues de rendu. Vous pouvez donc effectuer une redirection en clair en renvoyant simplement un RedirectAction dans le contrôleur, sans générer de vue.

153
JacquesB

Je sais que je suis en retard, mais je n'ai jamais eu cette erreur si mon Response.Redirect est dans un bloc Try...Catch.

Ne mettez jamais un Response.Redirect dans un bloc Try ... Catch. C'est une mauvaise pratique

Modifier

En réponse au commentaire de @ Kiquenet, voici ce que je ferais comme alternative à l'insertion de Response.Redirect dans le bloc Try ... Catch.

Je diviserais la méthode/fonction en deux étapes.

La première étape du bloc Try ... Catch exécute les actions demandées et définit une valeur de "résultat" pour indiquer le succès ou l'échec des actions.

La deuxième étape en dehors du bloc Try ... Catch effectue la redirection (ou ne le fait pas) en fonction de la valeur du "résultat".

Ce code est loin d'être parfait et ne devrait probablement pas être copié car je ne l'ai pas testé

public void btnLogin_Click(UserLoginViewModel model)
{
    bool ValidLogin = false; // this is our "result value"
    try
    {
        using (Context Db = new Context)
        {
            User User = new User();

            if (String.IsNullOrEmpty(model.EmailAddress))
                ValidLogin = false; // no email address was entered
            else
                User = Db.FirstOrDefault(x => x.EmailAddress == model.EmailAddress);

            if (User != null && User.PasswordHash == Hashing.CreateHash(model.Password))
                ValidLogin = true; // login succeeded
        }
    }
    catch (Exception ex)
    {
        throw ex; // something went wrong so throw an error
    }

    if (ValidLogin)
    {
        GenerateCookie(User);
        Response.Redirect("~/Members/Default.aspx");
    }
    else
    {
        // do something to indicate that the login failed.
    }
}
30
Ortund

Response.Redirect() lève une exception pour annuler la demande en cours.

Cet article KB décrit ce comportement (également pour les méthodes Request.End() et Server.Transfer()).

Pour Response.Redirect() il existe une surcharge:

Response.Redirect(String url, bool endResponse)

Si vous transmettez endResponse = false , l'exception n'est pas levée (mais le moteur d'exécution continue de traiter la demande en cours).

Si endResponse = true (ou si l'autre surcharge est utilisée), l'exception est levée et la demande en cours est immédiatement terminée.

8
M4N

C’est ainsi que fonctionne Response.Redirect (url, true). Il lève l'exception ThreadAbortException pour abandonner le thread. Ignorez cette exception. (Je suppose que c'est un gestionnaire d'erreurs global/enregistreur où vous le voyez?)

Une discussion connexe intéressante Response.End () est-il considéré comme dangereux?

7
Martin Smith

Voici le ligne officielle sur le problème (je ne pouvais pas trouver le dernier, mais je ne pense pas que la situation a changé pour les versions ultérieures de .net)

6
spender

j'ai même essayé d'éviter cela, juste au cas où faire l'abandon sur le fil manuellement, mais je préfère le laisser avec le "CompleteRequest" et continuer - mon code a des commandes de retour après les redirections de toute façon. Donc cela peut être fait

public static void Redirect(string VPathRedirect, global::System.Web.UI.Page Sender)
{
    Sender.Response.Redirect(VPathRedirect, false);
    global::System.Web.UI.HttpContext.Current.ApplicationInstance.CompleteRequest();
}
2
SammuelMiranda

J'ai aussi essayé une autre solution, mais une partie du code a été exécutée après la redirection.

public static void ResponseRedirect(HttpResponse iResponse, string iUrl)
    {
        ResponseRedirect(iResponse, iUrl, HttpContext.Current);
    }

    public static void ResponseRedirect(HttpResponse iResponse, string iUrl, HttpContext iContext)
    {
        iResponse.Redirect(iUrl, false);

        iContext.ApplicationInstance.CompleteRequest();

        iResponse.BufferOutput = true;
        iResponse.Flush();
        iResponse.Close();
    }

Donc si besoin d'empêcher l'exécution de code après la redirection

try
{
   //other code
   Response.Redirect("")
  // code not to be executed
}
catch(ThreadAbortException){}//do there id nothing here
catch(Exception ex)
{
  //Logging
}
2
Maxim Lavrov

Ce que je fais, c'est intercepter cette exception, ainsi que d'autres exceptions possibles. J'espère que cela aidera quelqu'un.

 catch (ThreadAbortException ex1)
 {
    writeToLog(ex1.Message);
 }
 catch(Exception ex)
 {
     writeToLog(ex.Message);
 }
1
Jorge

J'ai eu ce problème aussi. Essayez d'utiliser Server.Transfer au lieu de Response.Redirect Travaillé pour moi

0
Marko