web-dev-qa-db-ja.com

Response.RedirectがSystem.Threading.ThreadAbortExceptionを引き起こす理由

Response.Redirect(...)を使用してフォームを新しいページにリダイレクトすると、エラーが発生します。

タイプ 'System.Threading.ThreadAbortException'の最初のチャンス例外がmscorlib.dllで発生しました
mscorlib.dllでタイプ 'System.Threading.ThreadAbortException'の例外が発生しましたが、ユーザーコードでは処理されませんでした

私の理解では、このエラーは、ウェブサーバーがresponse.redirectが呼び出されたページの残りを中止することによって引き起こされているということです。

EndResponseと呼ばれるResponse.Redirectに2番目のパラメーターを追加できることを知っています。 endResponseをTrueに設定してもエラーは発生しますが、Falseに設定するとエラーは発生しません。確かに、それはウェブサーバーがリダイレクト先のページの残りを実行していることを意味します。控えめに言っても非効率的であると思われるでしょう。これを行うためのより良い方法はありますか? Response.Redirect以外の何か、またはThreadAbortExceptionを取得しない場所で古いページの読み込みを強制的に停止する方法はありますか?

223
Ben Hoffman

正しいパターンは、endResponse = falseでリダイレクトオーバーロードを呼び出し、IISパイプラインに呼び出しを行って、制御を戻したらEndRequestステージに直接進む必要があることです。

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

このブログ投稿 Thomas Marquardtから、Application_Errorハンドラー内でリダイレクトする特殊なケースを処理する方法など、追加の詳細が提供されています。

321
Joel Fillmore

ASP.Net WebFormsのRedirect問題に対するnoシンプルでエレガントなソリューションがあります。 DirtyソリューションとTediousソリューションから選択できます

DirtyResponse.Redirect(url)はブラウザーにリダイレクトを送信し、ThreadAbortedExceptionをスローして現在のスレッドを終了します。したがって、Redirect()呼び出しを過ぎてコードは実行されません。欠点:悪い習慣であり、このようなスレッドを強制終了することはパフォーマンスに影響します。また、ThreadAbortedExceptionsは例外ログに表示されます。

Tedious:推奨される方法は、Response.Redirect(url, false)を呼び出してからContext.ApplicationInstance.CompleteRequest()を呼び出すことです。ただし、コードの実行は続行され、ページライフサイクルの残りのイベントハンドラーは引き続き実行されます。 (たとえば、Page_Loadでリダイレクトを実行すると、ハンドラーの残りが実行されるだけでなく、Page_PreRenderなども呼び出されます。レンダリングされたページはブラウザーに送信されません。追加の処理を回避するには、たとえば、ページにフラグを設定し、処理を行う前に後続のイベントハンドラーにこのフラグをチェックさせます。

CompleteRequestのドキュメントには、「ASP.NETが実行のHTTPパイプラインチェーンですべてのイベントとフィルタリングをバイパスすることを示しています」。 HTTPフィルターとモジュールをさらにバイパスしますが、現在のpageライフサイクルでさらにイベントをバイパスしません。

より深い問題は、WebFormsに抽象化のレベルが欠けていることです。イベントハンドラーを使用しているときは、出力するページを作成中です。別のページを生成するために部分的に生成されたページを終了しているため、イベントハンドラーでのリダイレクトは見苦しくなります。制御フローはビューのレンダリングとは別個であるため、MVCにはこの問題はありません。したがって、ビューを生成せずに、コントローラーでRedirectActionを返すだけでクリーンリダイレクトを実行できます。

150
JacquesB

遅れていることはわかっていますが、Response.RedirectTry...Catchブロックにある場合にのみ、このエラーが発生しました。

Response.RedirectをTry ... Catchブロックに配置しないでください。それは悪い習慣です

編集

@Kiquenetのコメントに応答して、Response.RedirectをTry ... Catchブロックに配置する代わりに私が行うことは次のとおりです。

メソッド/関数を2つのステップに分割します。

Try ... Catchブロック内のステップ1は、要求されたアクションを実行し、アクションの成功または失敗を示す「結果」値を設定します。

Try ... Catchブロックの外側のステップ2は、「結果」値が何であるかに応じてリダイレクトを行います(または行いません)。

このコードは完全とはほど遠いものであり、テストしていないのでおそらくコピーすべきではありません

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

Response.Redirect()は、現在の要求を中止するために例外をスローします。

この KB記事 は、この動作について説明しています(Request.End()およびServer.Transfer()メソッドについても)。

Response.Redirect()にはオーバーロードが存在します:

Response.Redirect(String url, bool endResponse)

endResponse = falseを渡すと、例外はスローされません(ただし、ランタイムは現在のリクエストの処理を続行します)。

endResponse = trueの場合(または他のオーバーロードが使用されている場合)、例外がスローされ、現在のリクエストはすぐに終了します。

8
M4N

これは、Response.Redirect(url、true)の仕組みです。 ThreadAbortExceptionをスローして、スレッドを中止します。その例外を無視してください。 (私はそれがあなたがそれを見るいくつかのグローバルエラーハンドラ/ロガーだと思いますか?)

関連する興味深い議論 Response.End()は有害と考えられますか?

7
Martin Smith

問題に関する 公式行 です(最新版は見つかりませんでしたが、.netの以降のバージョンでは状況が変わったとは思いません)

6
spender

私はスレッドを手動で中止する場合にのみ、これを回避しようとしましたが、「CompleteRequest」でそれを残して続行します-私のコードにはリダイレクト後にリターンコマンドがあります。これができる

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

また、他の解決策も試しましたが、リダイレクト後に実行されるコードの一部があります。

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

リダイレクト後のコード実行を防ぐ必要がある場合

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

私がしていることは、この例外を別の可能な例外と一緒にキャッチすることです。これが誰かを助けることを願っています。

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

私もその問題を抱えていました。 Response.Redirect Workedの代わりにServer.Transferを使用してみてください

0
Marko