Perché Response.Redirect causa System.Threading.ThreadAbortException?


230

Quando utilizzo Response.Redirect (...) per reindirizzare il mio modulo a una nuova pagina, visualizzo l'errore:

Si è verificata un'eccezione della prima possibilità di tipo "System.Threading.ThreadAbortException" in mscorlib.dll
Un'eccezione del tipo "System.Threading.ThreadAbortException" si è verificata in mscorlib.dll ma non è stata gestita nel codice utente

La mia comprensione di questo è che l'errore è stato causato dal server web che ha interrotto il resto della pagina su cui è stato chiamato response.redirect.

So di poter aggiungere un secondo parametro a Response.Redirectquello che si chiama endResponse. Se imposto endResponse su True ottengo ancora l'errore, ma se lo imposto su False non lo faccio. Sono abbastanza sicuro che ciò significhi che il server web sta eseguendo il resto della pagina da cui ho reindirizzato. Il che sembrerebbe inefficiente per non dire altro. C'è un modo migliore per farlo? Qualcos'altro oltre Response.Redirecto c'è un modo per forzare il caricamento della vecchia pagina in cui non otterrò un ThreadAbortException?

Risposte:


332

Il modello corretto è chiamare il sovraccarico di reindirizzamento con endResponse = false ed effettuare una chiamata per dire alla pipeline IIS che dovrebbe avanzare direttamente allo stadio EndRequest una volta restituito il controllo:

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

Questo post sul blog di Thomas Marquardt fornisce ulteriori dettagli, incluso come gestire il caso speciale di reindirizzamento all'interno di un gestore Application_Error.


6
Esegue il codice dopo Context.ApplicationInstance.CompleteRequest();. Perché? Dovrò returndal gestore dell'evento in modo condizionale?
IsmailS,

4
@Ismail: la vecchia versione di Redirect genera una ThreadAbortException per impedire l'esecuzione di qualsiasi codice successivo. La nuova versione preferita non viene generata, ma sei responsabile di restituire il controllo in anticipo se hai un codice aggiuntivo nel gestore.
Joel Fillmore,

12
Penso che sia più preciso dire "il secondo sovraccarico" piuttosto che la The old version of Redirectfrase che usi nel tuo commento, non è come se MS abbia cambiato l'implementazione, è solo un altro sovraccarico.
BornToCode

2
Non penso che questo sia un modello ideale. Stai chiedendo alla pagina di non terminare la risposta e continuare l'esecuzione e quindi completare la richiesta a livello di codice. Ma per quanto riguarda il rendering della pagina aspx e dei gestori di eventi? non terminando la risposta significa, finirà per eseguire il rendering della pagina aspx prima di colpire "completeRequest ()". Ora, se sto usando una proprietà lato server nella mia pagina, dì una variabile di sessione per determinare un accesso valido, che se scade genererà un'eccezione nulla prima di reindirizzare. E l'unico modo per risolverlo è riportare endResponse su true.
Abs

1
Stava per votare questa risposta, ma quel codice di pagina ha continuato ad essere eseguito. Questo non è l'ideale nel mio caso. Molto più pulito per gestire o ignorare "ThreadAbortException"
DaniDev

159

Non esiste una soluzione semplice ed elegante al Redirectproblema in ASP.Net WebForms. Puoi scegliere tra la soluzione Dirty e la soluzione Tedious

Sporco : Response.Redirect(url)invia un reindirizzamento al browser, quindi lancia a ThreadAbortedExceptionper terminare il thread corrente. Quindi nessun codice viene eseguito oltre la chiamata Redirect (). Lati negativi: è una cattiva pratica e ha implicazioni sulle prestazioni nell'uccidere thread come questo. Inoltre, ThreadAbortedExceptionsverrà visualizzato nella registrazione delle eccezioni.

Tedious : il modo consigliato è chiamare, Response.Redirect(url, false)quindi l' Context.ApplicationInstance.CompleteRequest()esecuzione del codice continuerà e il resto dei gestori di eventi nel ciclo di vita della pagina verrà comunque eseguito. (Ad esempio, se esegui il reindirizzamento in Page_Load, non solo verrà eseguito il resto del gestore, Page_PreRender e così via verranno comunque chiamati - la pagina renderizzata non verrà semplicemente inviata al browser. Puoi evitare l'elaborazione aggiuntiva ad es. impostando un flag sulla pagina, quindi lasciare che i gestori di eventi successivi controllino questo flag prima di eseguire qualsiasi elaborazione.

(La documentazione CompleteRequestafferma che " Fa in modo che ASP.NET ignori tutti gli eventi e i filtri nella catena di esecuzione della pipeline HTTP ". Ciò può essere facilmente frainteso. Ignora ulteriori filtri e moduli HTTP, ma non ignora altri eventi nel ciclo di vita della pagina corrente .)

Il problema più profondo è che WebForms non ha un livello di astrazione. Quando ti trovi in ​​un gestore eventi, sei già in procinto di creare una pagina per l'output. Il reindirizzamento in un gestore eventi è brutto perché si sta terminando una pagina parzialmente generata per generare una pagina diversa. MVC non presenta questo problema poiché il flusso di controllo è separato dalle viste di rendering, quindi è possibile eseguire un reindirizzamento pulito semplicemente restituendo a RedirectActionnel controller, senza generare una vista.


7
Credo che la migliore descrizione dei webform che abbia mai sentito sia stata "lie sauce".
Mcfea,

9
Adoro la quantità di dettagli in questa risposta. Meglio della risposta accettata
Jess,

Se si utilizza l' opzione dirty , è possibile disattivare l'interruzione su ThreadAbortException in Visual Studio. DEBUG> Eccezioni ... . Espandere CLR> System.Threading> Deselezionare System.Threading.ThreadAbortException .
Jess,

grazie a dio abbiamo qualcuno con la risposta corretta, e questa dovrebbe essere la risposta più votata.
Abs

1
Nel mio caso questa eccezione non si presenta per ogni volta, solo per poche volte tra di essa si sta verificando. Mezzi Se e facendo clic sullo stesso pulsante dell'applicazione Live funziona, ma quando si fa clic sullo stesso collegamento e sullo stesso pulsante da un'altra macchina, viene visualizzato System.Threading.ThreadAbortException. Qualche idea sul perché non accada ogni volta ??
Sagar Shirke,

33

So di essere in ritardo, ma ho mai avuto questo errore solo se il mio Response.Redirectè in un Try...Catchblocco.

Non inserire mai un Response.Redirect in un blocco Try ... Catch. È una cattiva pratica

modificare

In risposta al commento di @ Kiquenet, ecco cosa farei in alternativa a inserire Response.Redirect nel blocco Try ... Catch.

Suddividerei il metodo / funzione in due passaggi.

Il passaggio 1 all'interno del blocco Try ... Catch esegue le azioni richieste e imposta un valore "risultato" per indicare l'esito positivo o negativo delle azioni.

Passaggio 2 all'esterno del blocco Try ... Catch esegue il reindirizzamento (o non lo fa) in base al valore "risultato".

Questo codice è tutt'altro che perfetto e probabilmente non dovrebbe essere copiato poiché non l'ho provato

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.
    }
}

@Kiquenet, vedi la mia risposta aggiornata per un esempio di cosa farei. Per non dire che è il miglior corso, ma credo sia una valida alternativa.
Ortund,

Non ho avuto il problema fino a quando non ho avvolto il mio codice in un tentativo, cattura ... Mi chiedo quali altre chiamate di codice causano questo comportamento in .NET
interessante-nome-qui

8

Response.Redirect() genera un'eccezione per interrompere la richiesta corrente.

Questo articolo della KB descrive questo comportamento (anche per i metodi Request.End()e Server.Transfer()).

Perché Response.Redirect()esiste un sovraccarico:

Response.Redirect(String url, bool endResponse)

Se si passa endResponse = false , l'eccezione non viene generata (ma il runtime continuerà a elaborare la richiesta corrente).

Se endResponse = true (o se viene utilizzato l'altro sovraccarico), viene generata l'eccezione e la richiesta corrente verrà immediatamente chiusa.


7

Ecco la linea ufficiale sul problema (non sono riuscito a trovare l'ultimo, ma non credo che la situazione sia cambiata per le versioni successive di .net)


5
@svick Indipendentemente dalla putrefazione dei link, solo le risposte ai link non sono risposte eccezionali. meta.stackexchange.com/q/8231 I think that links are fantastic, but they should never be the only piece of information in your answer.
Ryan Gates

7

Ecco come Response.Redirect(url, true) funziona. Lancia il ThreadAbortExceptionper interrompere il thread. Ignora quell'eccezione. (Suppongo che sia un gestore / logger di errori globale dove lo vedi?)

Un'interessante discussione correlata è Response.End()considerata dannosa? .


4
L'interruzione di un thread sembra un modo molto pesante per gestire la fine prematura della risposta. Trovo strano che il framework non preferisca riutilizzare il thread invece di crearne uno nuovo per sostituirlo.
spender

3

Inoltre ho provato un'altra soluzione, ma parte del codice eseguito dopo il reindirizzamento.

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

Quindi, se è necessario impedire l'esecuzione del codice dopo il reindirizzamento

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

1
segui semplicemente la risposta di Jorge. Ciò rimuoverà automaticamente la registrazione dell'eccezione di interruzione del thread.
Maxim Lavrov,

Quando qualcuno gli chiede perché riceve un'eccezione, dicendogli di giocare solo con try..catch non è una risposta. Vedi la risposta accettata Ho commentato la tua risposta durante la recensione di "risposta tardiva"
manuell

Questo ha lo stesso effetto di mettere falso per il secondo argomento di Response.Redirect, ma "falso" è una soluzione migliore rispetto alla cattura di ThreadAbortException. Non vedo che ci sia mai una buona ragione per farlo in questo modo.
NickG,

2

ho anche cercato di evitarlo, nel caso in cui avessi interrotto manualmente il thread, ma preferisco lasciarlo con "CompleteRequest" e andare avanti - il mio codice ha comandi di ritorno comunque dopo i reindirizzamenti. Quindi questo può essere fatto

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

1

Quello che faccio è catturare questa eccezione, insieme ad altre possibili eccezioni. Spero che questo aiuti qualcuno.

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

2
Meglio evitare l' eccezione ThreadAbortException che catturare e non fare nulla ?
Kiquenet,

-1

Ho avuto anche quel problema.

Prova a usare Server.Transferinvece diResponse.Redirect

Ha funzionato per me.


2
Server.Transfer dovrebbe comunque lanciare ThreadAbortException: support.microsoft.com/kb/312629 , quindi non è una soluzione consigliata.
Joel Beckham,

9
Server.Transfer non invierà un reindirizzamento all'utente. Ha uno scopo completamente diverso!
Marcel,

1
Server.transfer e responce.redirect sono diversi
mzonerz il
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.