Response.RedirectでSystem.Threading.ThreadAbortExceptionが発生するのはなぜですか?


230

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

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

私の理解では、このエラーの原因は、ウェブサーバーがresponse.redirectが呼び出されたページの残りの部分を中止したことです。

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

回答:


332

正しいパターンは、endResponse = falseを指定してRedirectオーバーロードを呼び出し、呼び出しを行って、コントロールを返したら、EndRequestステージに直接進む必要があることをIISパイプラインに通知することです。

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

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


6
後にコードを実行しますContext.ApplicationInstance.CompleteRequest();。どうして?私がしなければならないreturn条件付きイベントハンドラから?
IsmailS 2010年

4
@Ismail:古いバージョンのRedirectはThreadAbortExceptionをスローして、後続のコードの実行を防止します。新しい優先バージョンではスローされませんが、ハンドラーに追加のコードがある場合は、早い段階で制御を返す必要があります。
Joel Fillmore

12
The old version of Redirectコメントで使用するフレーズではなく、「2番目のオーバーロード」と言う方が正確だと思います。MSが実装を変更したようではなく、単なる別のオーバーロードです。
BornToCode 2015

2
これは理想的なパターンではないと思います。応答を終了せずに実行を続行し、プログラムで要求を完了するようにページに要求しています。しかし、aspxページとイベントハンドラーのレンダリングについてはどうでしょうか。応答を終了しないことは、 "completeRequest()"を押す前にaspxページのレンダリングを終了することを意味します。ここで、ページでサーバー側のプロパティを使用している場合は、セッション変数を指定して有効なログインを確認します。有効期限切れの場合、リダイレクトする前にnull例外がスローされます。そして、それを修正する唯一の方法は、endResponseをtrueに戻すことです。
Abs

1
この回答に賛成票を投じようとしていましたが、そのページコードは実行され続けました。これは私の場合には理想的ではありません。"ThreadAbortException"を処理または無視するためのよりクリーンな
DaniDev

159

ASP.Net WebFormsの問題に対するシンプルでエレガントなソリューションはありませんRedirectDirty solutionとTedious solutionのどちらかを選択できます

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

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

CompleteRequestASP.NETが実行のHTTPパイプラインチェーン内のすべてのイベントとフィルタリングをバイパスする」と記載されているドキュメント。これは、誤解される可能性が高いです。さらにHTTPフィルターとモジュールをバイパスしますが、イベントをバイパスしません現在のページのライフサイクル。)

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


7
私が聞いたウェブフォームの最も良い説明は「嘘のタレ」だったと思います。
mcfea 2015年

9
私はこの答えの詳細が大好きです。 受け入れられた回答よりも優れている
Jess

ダーティオプションを使用する場合、Visual StudioでThreadAbortExceptionのブレークをオフにすることができます。 DEBUG>例外...。展開CLR> System.Threading>のチェックを外しSystem.Threading.ThreadAbortExceptionを
ジェス

適切な答えを持っている誰かがいる神に感謝し、これが最も高い投票された答えになるはずです。
Abs

1
私の場合、この例外は毎回発生するわけではなく、その間に数回発生します。つまり、Liveアプリケーションの同じボタンをクリックすると機能しますが、同じリンクと同じボタンが他のマシンからクリックされると、System.Threading.ThreadAbortExceptionが発生します。なぜそれが毎回起こらないのか?
Sagar Shirke 2017年

33

私は遅れていることを知っていますが、このエラー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.
    }
}

@Kiquenetは、私が行うことの例について、私の最新の回答を参照してください。それは最高のコースとは言えませんが、私が思うに実行可能な代替手段です。
Ortund、2016年

私のコードをtry、catchでラップするまで問題はありませんでした...他のコード呼び出しが.NETでこの動作を引き起こしているのではないかと思います
Interesting-name-here

8

Response.Redirect() 例外をスローして現在のリクエストを中止します。

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

以下のためにResponse.Redirect()過負荷が存在します:

Response.Redirect(String url, bool endResponse)

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

もし endResponse = trueの(または他の過負荷が使用されている場合)、例外がスローされ、現在の要求を直ちに終了されます。


7

ここだ公式のラインの問題では(私は最新のを見つけることができませんでしたが、私は状況は.NETそれ以降のバージョンの変更されているとは思いません)


5
@svickリンクの腐敗に関係なく、リンクのみの回答は本当に素晴らしい回答ではありません。meta.stackexchange.com/q/8231 I think that links are fantastic, but they should never be the only piece of information in your answer.
ライアンゲイツ

7

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

興味深い関連ディスカッションResponse.End()有害と見なされますか?


4
スレッドの中止は、応答の時期尚早の終了に対処するための非常に手間のかかる方法のようです。フレームワークが新しいスレッドを起動する代わりにスレッドを再利用する代わりに、スレッドを再利用することを好まないのは不思議です。
消費者は

3

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

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
}

1
ホルヘの回答をフォローしてください。これにより、スレッドアボート例外のロギングが削除されます。
Maxim Lavrov、2014年

誰かが例外を受け取る理由を尋ねたとき、try..catchで遊ぶように言うだけでは答えにはなりません。受け入れられた答えを見てください。私は「遅い答え」をレビューしている間にあなたの答えにコメントしました
manuell

これは、Response.Redirectの2番目の引数にfalseを設定するのと同じ効果がありますが、 "false"はThreadAbortExceptionをキャプチャするよりも優れたソリューションです。このようにするのに十分な理由があるとは思えません。
NickG 2016年

2

スレッドでAbortを手動で実行する場合に備えて、これを回避しようとしましたが、「CompleteRequest」を残して先に進みます-とにかくリダイレ​​クト後にコードがreturnコマンドを返します。これを行うことができます

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

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

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

2
キャッチして何もしないよりもThreadAbortException 例外を回避する方が良いですか?
Kiquenet 2015年

-1

私もその問題を抱えていました。

Server.Transfer代わりに使用してみてくださいResponse.Redirect

私のために働いた。


2
Server.Transferは引き続きThreadAbortException:support.microsoft.com/kb/312629をスローするはずなので、これは推奨されるソリューションではありません。
Joel Beckham

9
Server.Transferはユーザーにリダイレクトを送信しません。まったく別の目的があります!
マルセル

1
Server.transferとresponce.redirectは異なります
mzonerz
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.