为什么Response.Redirect导致System.Threading.ThreadAbortException?


230

当我使用Response.Redirect(...)将表单重定向到新页面时,出现错误:

mscorlib.dll中发生类型'System.Threading.ThreadAbortException'的第一次机会异常mscorlib.dll中发生类型'System.Threading.ThreadAbortException'
的异常,但未在用户代码中处理

我对此的理解是该错误是由Web服务器中止了response.redirect被调用的页面的其余部分引起的。

我知道我可以添加第二个参数Response.Redirect,称为endResponse。如果将endResponse设置为True,仍然会收到错误,但是如果将其设置为False,则不会。我相当确定,但这意味着Web服务器正在运行我重定向的其余页面。至少可以说这似乎效率低下。有一个更好的方法吗?除了Response.Redirect或是否有其他方法可以迫使旧页面停止加载而我无法得到ThreadAbortException

Answers:


332

正确的模式是使用endResponse = false调用Redirect重载,并进行调用以告知IIS管道,一旦返回控件,它应直接进入EndRequest阶段:

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

Thomas Marquardt的这篇博客文章提供了更多详细信息,包括如何处理Application_Error处理程序中重定向的特殊情况。


6
它在之后执行代码Context.ApplicationInstance.CompleteRequest();。为什么?我是否必须return有条件地离开事件处理程序?
IsmailS 2010年

4
@Ismail:旧版本的Redirect抛出ThreadAbortException以防止执行任何后续代码。不会抛出较新的首选版本,但是如果处理程序中包含其他代码,则您有责任尽早返回控件。
乔尔·菲尔莫尔

12
我认为说“第二重载”而不是The old version of Redirect您在评论中使用的短语更准确,这不像MS更改了实现,只是另一个重载。
BornToCode 2015年

2
我认为这不是理想的模式。您要页面不结束响应并继续执行,然后以编程方式完成请求。但是,aspx页面和事件处理程序的呈现方式又如何呢?没有结束响应,它将在点击“ completeRequest()”之前完成渲染aspx页面。现在,如果我在页面中使用服务器端属性,请说一个会话变量来确定有效的登录名,如果该登录名到期,将在重定向之前引发null异常。解决此问题的唯一方法是使endResponse恢复为true。
Abs

1
打算对这个答案进行投票,但是该页面代码继续执行。就我而言,这并不理想。处理或忽略“ ThreadAbortException”的方法更加干净
DaniDev

159

没有简单而优雅的解决Redirect问题在ASP.Net Web窗体。您可以在Dirty解决方案和Tedious解决方案之间进行选择

DirtyResponse.Redirect(url)将重定向发送到浏览器,然后抛出A ThreadAbortedException终止当前线程。因此,在Redirect()调用之后不会执行任何代码。缺点:这是一种不好的做法,可能会杀死此类线程,从而影响性能。另外,ThreadAbortedExceptions将显示在异常日志中。

乏味:推荐的方法是先调用Response.Redirect(url, false),然后调用,Context.ApplicationInstance.CompleteRequest()但是,代码执行将继续,并且页面生命周期中的其余事件处理程序仍将执行。(例如,如果您在Page_Load中执行重定向,不仅将执行其余的处理程序,还将调用Page_PreRender等-渲染的页面将不会发送到浏览器。例如,在页面上设置一个标志,然后让后续事件处理程序在进行任何处理之前先检查该标志。

(该文档CompleteRequest指出它“ 导致ASP.NET绕过HTTP执行链中的所有事件和过滤 ”。这很容易被误解。它确实绕过了其他HTTP过滤器和模块,但没有绕过其他事件。在当前页面生命周期中。)

更深层的问题是WebForms缺乏抽象层次。当您使用事件处理程序时,您已经在构建要输出的页面的过程中。在事件处理程序中重定向很丑陋,因为您要终止部分生成的页面以生成另一个页面。MVC没有此问题,因为控制流与渲染视图是分开的,因此您可以通过简单地RedirectAction在控制器中返回a 而无需生成视图来进行干净的重定向。


7
我相信我听说过的对Webform的最好描述是“谎言酱”。
mcfea 2015年

9
我喜欢这个答案中的细节。 比公认的答案更好
Jess

如果使用dirty选项,则可以在Visual Studio中关闭对ThreadAbortException的中断。 DEBUG>例外...。展开CLR> System.Threading>取消选中System.Threading.ThreadAbortException
杰西2015年

谢谢上帝,我们有一个正确答案的人,这应该是投票率最高的答案。
Abs

1
在我的情况下,这种异常并不是每次都出现,只是两次之间都发生了几次。意味着,如果单击Live应用程序的同一按钮,则它正在工作,但是当从另一台计算机单击相同的链接和相同的按钮时,它将给出System.Threading.ThreadAbortException。知道为什么它不会每次都发生吗?
Sagar Shirke'1

33

我知道我迟到了,但只有Response.Redirect在遇到Try...Catch障碍时才遇到此错误。

切勿将Response.Redirect放入Try ... Catch块中。这是不好的做法

编辑

为了回应@Kiquenet的评论,这是我将Response.Redirect放入Try ... Catch块的替代方法。

我将方法/功能分为两个步骤。

Try ... Catch块中的第一步执行请求的操作,并设置“结果”值以指示操作成功或失败。

Try ... Catch块之外的第二步根据“结果”值进行重定向(或不进行重定向)。

该代码远非完美,由于我未测试过,因此可能不应复制

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 '16

直到我尝试将代码包装好之后,才出现问题,赶上...我想知道其他代码调用会在.NET中导致此行为
有趣的名称,此处

8

Response.Redirect() 引发异常以中止当前请求。

知识库文章介绍了此行为(也针对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 2014年

这与将Response.Redirect的第二个参数设置为false具有相同的效果,但是“ false”是比捕获ThreadAbortException更好的解决方案。我认为没有足够的理由这样做。
NickG

2

我什至试图避免这种情况,以防万一手动在线程上执行中止操作,但我宁愿将其保留为“ 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();
}

1

我要做的是捕获此异常以及其他可能的异常。希望这对某人有帮助。

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

2
最好避免避免ThreadAbortException 异常,而不是不执行任何操作
Kiquenet

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.