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

当我使用Response.Redirect(…)redirect我的表单到一个新的页面时,我得到的错误:

在mscorlib.dll中发生types“System.Threading.ThreadAbortException”的第一个机会exception
mscorlib.dll中发生types“System.Threading.ThreadAbortException”的exception,但未在用户代码中处理

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

我知道我可以将第二个参数添加到名为endResponse的Response.Redirect 。 如果我设置endResponse为True,我仍然得到错误,但如果我将它设置为False,那么我不会。 我很确定,这意味着networking服务器正在运行我redirect的页面的其余部分。 至less可以说这似乎是低效率的。 有没有更好的方法来做到这一点? 除了Response.Redirect之外的东西还是有办法强制旧页面停止加载,我不会得到一个ThreadAbortException

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

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

Thomas Marquardt的这篇博文提供了更多的细节,包括如何处理在Application_Error处理程序中redirect的特殊情况。

ASP.Net WebForms中的Redirect问题没有简单而优雅的解决scheme。 您可以select解决scheme和繁琐的解决scheme

DirtyResponse.Redirect(url)发送一个redirect到浏览器,然后抛出一个ThreadAbortedException来终止当前的线程。 所以没有代码被执行通过redirect() – 调用。 缺点:这是不好的做法,并有性能影响杀死这样的线程。 此外, ThreadAbortedExceptions将显示在exceptionlogging。

乏味 :推荐的方法是调用Response.Redirect(url, false) ,然后Context.ApplicationInstance.CompleteRequest()然而,代码执行将继续,页面生命周期中的其余事件处理程序仍将被执行。 (例如,如果在Page_Load中执行redirect,则不仅会执行处理程序的其余部分,还会调用Page_PreRender等 – 所呈现的页面将不会被发送到浏览器,您可以避免额外的处理例如在页面上设置一个标志,然后让后续事件处理程序在进行任何处理之前检查该标志。

CompleteRequest的文档声明它“ 导致ASP.NET绕过HTTPpipe道执行过程中的所有事件和过滤 ”,这很容易被误解,它会绕过更多的HTTPfilter和模块,但是它不会绕过当前页面生命周期中的事件。)

更深层的问题是WebForms缺乏抽象层次。 当您处于事件处理程序中时,您已经在构build要输出的页面的过程中。 redirect到一个事件处理程序是丑陋的,因为你正在终止一个部分生成的页面,以便生成一个不同的页面。 MVC没有这个问题,因为控制stream与渲染视图是分开的,所以你可以通过简单地在控制器中返回一个RedirectAction来做一个干净的redirect,而不会产生一个视图。

我知道我迟到了,但是如果我的Response.RedirectTry...Catch块中,我只会遇到这个错误。

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

编辑

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

我将分解方法/function分为两个步骤。

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

Try … Catch块外的第二步根据“结果”值是什么进行redirect(或不)。

这个代码是不完美的,可能不应该复制,因为我没有testing它

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

Response.Redirect()抛出一个exception来中止当前的请求。

这篇知识库文章描述了这种行为(也针对Request.End()Server.Transfer()方法)。

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

 Response.Redirect(String url, bool endResponse) 

如果传递endResponse = false ,则不会引发exception(但运行时将继续处理当前请求)。

如果endResponse = true (或者使用其他重载),则引发exception,并且当前请求将立即终止。

这就是Response.Redirect(url,true)的工作原理。 它抛出ThreadAbortExceptionexception中止线程。 只要忽略这个例外。 (我认为这是一些全球error handling程序/logging器,你看到它?)

一个有趣的相关讨论是Response.End()认为有害吗?

这里是关于这个问题的官方文章(我找不到最新的,但是我不认为这个情况在.net的后续版本中已经改变了)

我所做的是抓住这个例外,再加上另一个可能的例外。 希望这可以帮助别人。

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

另外我尝试了其他解决scheme,但redirect后执行了一些代码。

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

所以如果需要防止redirect后的代码执行

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

我甚至试图避免这种情况,以防万一在线程手动中止,但我宁愿留下“CompleteRequest”,并继续前进 – 我的代码无论如何有redirect后返回命令。 所以这可以做到

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

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