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

时间:2010-05-05 22:02:44

标签: c# asp.net .net-3.5

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

  

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

我对此的理解是,错误是由于网络服务器中止了调用response.redirect的页面的其余部分。

我知道我可以向Response.Redirect添加一个名为endResponse的第二个参数。如果我将endResponse设置为True,我仍然会收到错误,但如果我将其设置为False,那么我不会。我很确定,这意味着网络服务器正在运行我重定向的页面的其余部分。至少可以说这似乎效率低下。有一个更好的方法吗?除Response.Redirect以外的其他内容还是有办法强制旧页面停止加载我无法获得ThreadAbortException的地方?

11 个答案:

答案 0 :(得分:319)

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

Response.Redirect(url, false);
Context.ApplicationInstance.CompleteRequest();
来自Thomas Marquardt的

This blog post提供了其他详细信息,包括如何处理在Application_Error处理程序中重定向的特殊情况。

答案 1 :(得分:150)

ASP.Net WebForms中的Redirect问题有没有简单而优雅的解决方案。您可以选择 Dirty 解决方案和繁琐解决方案

Response.Redirect(url)向浏览器发送重定向,然后抛出ThreadAbortedException以终止当前线程。因此,没有代码执行Redirect() - 调用。缺点:这是一种不好的做法,并且会对杀死这样的线程产生影响。此外,ThreadAbortedExceptions将显示在异常日志记录中。

繁琐:建议的方法是调用Response.Redirect(url, false)然后调用Context.ApplicationInstance.CompleteRequest()但是,代码执行将继续,页面生命周期中的其余事件处理程序仍将是执行。 (例如,如果您在Page_Load中执行重定向,则不仅会执行处理程序的其余部分,还会调用Page_PreRender等等 - 渲染的页面将不会被发送到浏览器。您可以避免额外的处理例如,在页面上设置一个标志,然后让后续事件处理程序在进行任何处理之前检查此标志。

CompleteRequest的文档声明它“导致ASP.NET绕过所有事件并在HTTP管道执行链中进行过滤”。这很容易被误解。绕过更多的HTTP过滤器和模块,但它不会绕过当前生命周期中的其他事件。)

更深层次的问题是WebForms缺乏抽象层次。当您在事件处理程序中时,您已经在构建要输出的页面的过程中。在事件处理程序中重定向很难看,因为您要终止部分生成的页面以生成不同的页面。 MVC没有这个问题,因为控制流与渲染视图是分开的,因此只需在控制器中返回RedirectAction而不生成视图,就可以进行干净的重定向。

答案 2 :(得分:29)

我知道我已经迟到了,但如果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.
    }
}

答案 3 :(得分:8)

Response.Redirect()抛出异常以中止当前请求。

KB article描述了此行为(也适用于Request.End()Server.Transfer()方法)。

对于Response.Redirect(),存在重载:

Response.Redirect(String url, bool endResponse)

如果传递 endResponse = false ,则不会抛出异常(但运行时将继续处理当前请求)。

如果 endResponse = true (或者如果使用了其他重载),则抛出异常并立即终止当前请求。

答案 4 :(得分:7)

这就是Response.Redirect(url,true)的工作原理。它抛出ThreadAbortException以中止线程。只是忽略那个例外。 (我认为它是一个全局错误处理程序/记录器,你在哪里看到它?)

一个有趣的相关讨论Is Response.End() considered harmful?

答案 5 :(得分:6)

这是关于问题的official line(我找不到最新的,但我不认为.net的更高版本的情况已经改变了)

答案 6 :(得分:2)

此外,我尝试了其他解决方案,但重定向后执行了一些代码。

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
}

答案 7 :(得分: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();
}

答案 8 :(得分:1)

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

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

答案 9 :(得分:0)

我也有这个问题。 尝试使用Server.Transfer而不是Response.Redirect 为我工作

答案 10 :(得分:-2)

我今天遇到了同样的问题。在我的项目中,我有两种类型的日志记录页面(简单用户和医生用户)。上周我完成了简单的用户日志记录页面,并且没有任何问题。本周我开始了医生登录页面。完成后,我再次检查了简单用户登录,发现它不起作用。我跟踪并意识到问题出在 response.redirect("simpleuser.aspx") (不要导航到 simpleuser.aspx)。 错误是(无法评估表达式,因为代码已优化或本机框架位于调用堆栈顶部。)我通过互联网检查了所有解决方案,没有运气......甚至“response.redirect(URL,false)” 我所做的,我只是将 simpleuser.aspx 复制到另一个目录....就是这样。完毕。 :)