ChatGPT解决这个技术问题 Extra ChatGPT

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

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

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

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

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


p
pseudocoder

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

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

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


它在 Context.ApplicationInstance.CompleteRequest(); 之后执行代码。为什么?我必须有条件地从事件处理程序中return吗?
@Ismail:旧版本的 Redirect 抛出 ThreadAbortException 以防止执行任何后续代码。较新的首选版本不会抛出异常,但如果您在处理程序中有其他代码,您有责任尽早返回控制权。
我认为说“第二个重载”而不是您在评论中使用的 The old version of Redirect 短语更准确,这不像 MS 改变了实现,它只是另一个重载。
我不认为这是一个理想的模式。您要求页面不要结束响应并继续执行,然后以编程方式完成请求。但是 aspx 页面和事件处理程序的呈现呢?不结束响应意味着,它将在点击“completeRequest()”之前完成呈现 aspx 页面。现在,如果我在我的页面中使用服务器端属性,请说一个会话变量来确定有效登录,如果过期将在重定向之前引发空异常。解决这个问题的唯一方法是让 endResponse 恢复为真。
打算投票赞成这个答案,但该页面代码继续执行。在我的情况下,这并不理想。处理或忽略“ThreadAbortException”要干净得多
J
JacquesB

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

DirtyResponse.Redirect(url) 向浏览器发送重定向,然后抛出 ThreadAbortedException 以终止当前线程。因此,在 Redirect() 调用之后不会执行任何代码。缺点:像这样杀死线程是不好的做法,并且会影响性能。此外,ThreadAbortedExceptions 将显示在异常记录中。

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

CompleteRequest 的文档指出它“导致 ASP.NET 绕过 HTTP 执行管道链中的所有事件和过滤”。这很容易被误解。它确实绕过了进一步的 HTTP 过滤器和模块,但它不会绕过当前 page 生命周期中的进一步事件。)

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


我相信我听过的对网络表单最好的描述是“谎言酱”。
如果您使用脏选项,您可以在 Visual Studio 中关闭 ThreadAbortException 的中断。调试 > 异常......展开 CLR > System.Threading > 取消选中 System.Threading.ThreadAbortException。
在我的情况下,这个异常并不是每次都发生,只是在它之间发生了几次。意味着如果单击 Live 应用程序的相同按钮,它正在工作,但是当从其他机器单击相同的链接和相同的按钮时,它会给出 System.Threading.ThreadAbortException。知道为什么它不是每次都发生吗?
如果您传递 false,并且不费心检查标志,那么浏览器将收到整个页面的标题中带有 302 状态代码 - 已找到。恶意用户只需将状态更改为 200,就可以访问整个页面,这是非常不安全的。
如果 Context 无法为您解决,请尝试 HttpContext
T
TylerH

我知道我迟到了,但我只有在我的 Response.Redirect 位于 Try...Catch 块中时才会出现此错误。

永远不要将 Response.Redirect 放入 Try...Catch 块中。这是不好的做法

作为将 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.
    }
}

直到我将代码包装在 try, catch 中才出现问题...我想知道其他代码调用会导致 .NET 中的这种行为
这也将发生在 if-else 语句中,而不仅仅是 try-catch。我相信这是任何条件语句,甚至可能在任何事件处理程序中。无论如何,执行您建议的更改并不能解决问题,因为它仍在条件和事件处理程序中。
M
M4N

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

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

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

Response.Redirect(String url, bool endResponse)

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

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


s
spender

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


@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.
D
Dale K

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

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


中止线程似乎是处理过早结束响应的一种非常严厉的方法。我觉得奇怪的是框架不喜欢重用线程而不是旋转一个新线程来代替它。
S
SammuelMiranda

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

可能是 public class MyBasePage : Page 中的受保护方法
不是System.Web.UI.HttpContext。真的System.Web.HttpContext
M
Maxim Lavrov

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

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
}

只需跟进豪尔赫的回答。这将最终删除线程中止异常的日志记录。
当有人问他为什么会得到一个异常时,告诉他只玩 try..catch 不是答案。请参阅已接受的答案。我在查看“迟到的答案”时评论了您的答案
这与为 Response.Redirect 的第二个参数设置 false 具有相同的效果,但“false”是比捕获 ThreadAbortException 更好的解决方案。我认为没有充分的理由这样做。
J
Jorge

我所做的是捕获这个异常以及另一个可能的异常。希望这对某人有所帮助。

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

最好避免 ThreadAbortException 异常而不是 catch 并且什么都不做?
D
Dale K

我也有这个问题。

尝试使用 Server.Transfer 而不是 Response.Redirect

为我工作。


Server.Transfer 仍应抛出 ThreadAbortException: support.microsoft.com/kb/312629,因此不是推荐的解决方案。
Server.Transfer 不会向用户发送重定向。它完全有不同的目的!
Server.transfer 和 response.redirect 不同