Response.End() 被认为是有害的吗?

Posted

技术标签:

【中文标题】Response.End() 被认为是有害的吗?【英文标题】:Is Response.End() considered harmful? 【发布时间】:2010-11-08 10:27:51 【问题描述】:

This KB Article 表示 ASP.NET 的 Response.End() 中止线程。

Reflector 显示它看起来像这样:

public void End()

    if (this._context.IsInCancellablePeriod)
    
        InternalSecurityPermissions.ControlThread.Assert();
        Thread.CurrentThread.Abort(new HttpApplication.CancelModuleException(false));
    
    else if (!this._flushing)
    
        this.Flush();
        this._ended = true;
        if (this._context.ApplicationInstance != null)
        
            this._context.ApplicationInstance.CompleteRequest();
        
    

这对我来说似乎很苛刻。正如知识库文章所说,应用程序中Response.End() 之后的任何代码都不会被执行,这违反了最小惊讶原则。这几乎就像 WinForms 应用程序中的Application.Exit()Response.End() 导致的线程中止异常是无法捕获的,因此将代码包围在 try...finally 中不会满足。

这让我想知道我是否应该始终避免Response.End()

谁能建议我什么时候应该使用Response.End(),什么时候应该使用Response.Close(),什么时候应该使用HttpContext.Current.ApplicationInstance.CompleteRequest()

参考:Rick Strahl's blog entry.


根据我收到的意见,我的回答是,是的,Response.End 是有害的,但它在某些有限的情况下很有用。

使用Response.End() 作为不可捕获的抛出,在异常情况下立即终止HttpResponse。在调试期间也很有用。 避免Response.End() 完成例行回复。 使用Response.Close() 立即关闭与客户端的连接。根据this MSDN blog post,此方法不适用于正常的 HTTP 请求处理。您不太可能有充分的理由调用此方法。 使用CompleteRequest() 结束一个正常的请求。 CompleteRequest 导致 ASP.NET 管道在当前 HttpApplication 事件完成后跳转到 EndRequest 事件。因此,如果您调用CompleteRequest,然后在响应中写入更多内容,则写入内容将发送给客户端。

编辑 - 2011 年 4 月 13 日

此处提供了进一步的说明:

Useful post on MSDN Blog Useful analysis by Jon Reid

【问题讨论】:

不知道自这个答案以来发生了什么变化,但我正在赶上 Response.End ThreadAbortException 就好了。 请记住,Response.RedirectServer.Transfer 都调用 Response.End,也应该避免使用。 【参考方案1】:

TL;DR

最初我建议您只需将所有调用替换为 [Response.End] 与 [...] CompleteRequest() 调用,但如果你想避免 回发处理和 html 呈现,您需要添加 [...] 覆盖作为 好吧。

Jon Reid, "最终分析"


根据 MSDN、Jon Reid 和 Alain Renon:

ASP.NET Performance - Exception Management - Write Code That Avoids Exceptions

Server.Transfer、Response.Redirect、Response.End 方法都会引发 例外。这些方法中的每一个都在内部调用 Response.End。呼吁 Response.End,反过来,causes a ThreadAbortException exception。

ThreadAbortException Solution

HttpApplication.CompleteRequest() 设置一个变量,使线程 跳过 HttpApplication 事件管道 [--] 中的大多数事件,而不是 页面事件链,但应用程序事件链。

...

创建一个类级别的变量来标记页面是否应该终止,然后 在处理您的事件或呈现您的页面之前检查变量。 [...] 我只推荐overriding the RaisePostBackEvent and Render methods

Response.End 和 Response.Close 在正常请求处理时不使用 性能很重要。 Response.End 是一种方便的、严厉的手段 以相关的性能损失终止请求处理。 Response.Close 用于立即终止 IIS/socket 上的 HTTP 响应 水平并导致诸如 KeepAlive 之类的问题。

结束 ASP.NET 请求的推荐方法是 HttpApplication.CompleteRequest。请记住,ASP.NET 渲染将具有 手动跳过,因为 HttpApplication.CompleteRequest 跳过了其余的 IIS/ASP.NET 应用程序管道,而不是 ASP.NET 页面管道(即 应用管道中的一个阶段)。


代码

Copyright © 2001-2007, C6 Software, Inc 尽我所能。


参考

HttpApplication.CompleteRequest

使 ASP.NET 绕过 HTTP 管道链中的所有事件和过滤 执行,直接执行EndRequest事件。

Response.End

此方法只是为了与 ASP 兼容——也就是说, 与之前基于 COM 的 Web 编程技术的兼容性 ASP.NET.preceded ASP.NET。 [强调补充]

Response.Close

此方法以突然的方式终止与客户端的连接,并且是 不适用于正常的 HTTP 请求处理。 [强调补充]

【讨论】:

>请记住,必须手动跳过 ASP.NET 呈现,因为 HttpApplication.CompleteRequest 跳过了 IIS/ASP.NET 应用程序管道的其余部分,而不是 ASP.NET 页面管道(即应用程序管道中的一个阶段)。你是如何做到这一点的? 查看 Jon Reid 演示如何设置标志并覆盖页面的 RaisePostBackEvent 和 Render 方法以在需要时跳过正常实现的代码链接。 (您可能会在所有应用程序页面都应继承自的基类中执行此操作。)web.archive.org/web/20101224113858/http://www.c6software.com/… 重申一下:HttpApplication.CompleteRequest 不会像 Response.End 那样终止响应。 HttpApplication.CompleteRequest 也不会停止代码流,因此后续行会继续运行。这可能不会影响浏览器所看到的内容,但如果这些行进行任何其他处理,它可能会非常混乱。 我想不出 Web 表单是被设计破坏的。什么是性能下降,调用 Response.End() 或让页面加载所有内容然后抑制响应?我看不到 Response.End() 在这里“更”有害的地方。此外,Microsoft 将 `ThreadAbortedException' 视为正常事件,从以下代码可以明显看出:referencesource.microsoft.com/#System.Web/UI/Page.cs,4875 反对 Response.End() 的一件事是它可能无法中止响应,这可能导致偶尔显示响应。 【参考方案2】:

这个问题出现在所有关于 response.end 信息的谷歌搜索的顶部附近,因此对于像我这样希望发布 CSV/XML/PDF 等以响应事件而不呈现整个 ASPX 页面的其他搜索,这就是如何我做。 (对于 IMO 如此简单的任务,覆盖渲染方法过于复杂)

// Add headers for a csv file or whatever
Response.ContentType = "text/csv"
Response.AddHeader("Content-Disposition", "attachment;filename=report.csv")
Response.AddHeader("Pragma", "no-cache")
Response.AddHeader("Cache-Control", "no-cache")

// Write the data as binary from a unicode string
Dim buffer As Byte()
buffer = System.Text.Encoding.Unicode.GetBytes(csv)
Response.BinaryWrite(buffer)

// Sends the response buffer
Response.Flush()

// Prevents any other content from being sent to the browser
Response.SuppressContent = True

// Directs the thread to finish, bypassing additional processing
HttpContext.Current.ApplicationInstance.CompleteRequest()

【讨论】:

您不应该使用 APSX 页面来执行此操作。这是很多浪费的努力。您应该使用 ASMX 或 Web 服务,而不是 ASPX 页面。 这似乎是最简单实现的答案。关键是 Response.SuppressContent = True。 @mattmanser - 为同一资源的不同表示设置单独的页面并不总是容易/最好/可取的。想想 REST 等。如果客户端通过 header 或 param 表明他们想要 csv、xml,这种方法肯定是最好的,同时仍然通过 asp.net 的正常渲染工具提供 html 支持。 这对我不起作用。我有一个使用 Response.End() 的页面,但使用了 Response.Close()、Response.Flush() 的各种组合。如果我在响应上有一个 GzipStream 过滤器,HttpContext.Current.ApplicationInstance.CompleteRequest() 和其他各种事情都不起作用。似乎正在发生的事情是该页面仍在与我的文件一起输出。我终于覆盖了 Render() 函数(为空白)并为我解决了它。 CompleteRequest 会跳过部分应用程序管道,但仍会在页面渲染过程的其余部分运行,它不像 response.end 那样立即停止,它更优雅。在本页的其他答案中有更深入的解释。【参考方案3】:

如果您在您的应用程序中使用了异常记录器,它将被来自这些良性 Response.End() 调用的 ThreadAbortExceptions 淡化。我认为这是微软的表达方式“Knock it off!”。

如果有一些异常情况并且不可能采取其他行动,我只会使用Response.End()。也许那时,记录此异常实际上可能表示警告。

【讨论】:

【参考方案4】:

关于“我仍然不知道 Response.Close 和 CompleteRequest() 之间的区别”的问题,我会说:

更喜欢 CompleteRequest(),不要使用 Response.Close()。

请参阅following article 了解此案例的完整摘要。

请注意,即使在调用 CompleteRequest() 之后,某些文本(例如从 ASPX 代码中重新生成)也会附加到响应输出流中。您可以通过覆盖 following article 中所述的 Render 和 RaisePostBackEvent 方法来防止它。

顺便说一句:我同意防止使用 Response.End(),尤其是在将数据写入 http 流以模拟文件下载时。我们过去一直使用 Response.End(),直到我们的日志文件充满了 ThreadAbortExceptions。

【讨论】:

我有兴趣按照您的描述覆盖 Render,但“以下文章”的链接已失效。也许您可以更新您的条目? 抱歉回复晚了。我不完全记得那篇文章中的内容。但是,我在 webarchive.org 上找到了它:web.archive.org/web/20101224113858/http://www.c6software.com/…【参考方案5】:

我不同意“Response.End 有害”的说法。这绝对没有害处。 Response.End 照它说的做;它结束页面的执行。使用反射器来查看它是如何实现的应该仅被视为具有指导意义。


我的 2cent 建议避免使用 Response.End() 作为控制流。使用 Response.End() 如果您需要停止请求执行并注意(通常)* 没有代码会在该点之后执行。


* Response.End() 和 ThreadAbortExceptions。

Response.End() 抛出 ThreadAbortException 作为其当前实现的一部分(如 OP 所述)。

ThreadAbortException 是一个可以被捕获的特殊异常,但是 它将在 catch 块结束时自动再次升起。

要了解如何编写必须处理 ThreadAbortExceptions 的代码,请参阅 @Mehrdad 对 SO How can I detect a threadabortexception in a finally block 的回复,其中他引用了 RuntimeHelpers.ExecuteCodeWithGuaranteedCleanup Method 和 Constrained Execution Regions


提到的Rick Strahl article 具有指导意义,请务必阅读 cmets。请注意,Strahl 的问题是具体的。他想将数据发送给客户端(图像),然后处理命中跟踪数据库更新,这不会减慢图像的服务速度,这使他在调用 Response.End 后遇到了问题。

【讨论】:

我们看到这篇帖子***.com/questions/16731745/… 建议使用 Response.SuppressContent = True HttpContext.Current.ApplicationInstance.CompleteRequest() 而不是 Response.End()【参考方案6】:

我从未考虑过使用 Response.End() 来控制程序流程。

但是 Response.End() 可能很有用,例如在向用户提供文件时。

您已将文件写入响应,并且您不希望将任何其他内容添加到响应中,因为它可能会损坏您的文件。

【讨论】:

我了解 API 需要说“响应已完成”。但是 Response.End() 也会执行线程中止。这是问题的症结所在。什么时候将这两件事结合起来是个好主意?【参考方案7】:

我之前在 .NET 和 Classic ASP 中都使用过 Response.End() 来强制结束事情。例如,当有一定数量的登录尝试时,我会使用它。或者当从未经身份验证的登录访问安全页面时(粗略示例):

    if (userName == "")
    
        Response.Redirect("......");
        Response.End();
    
    else
    
      .....

当我使用 Flush 向用户提供文件时,End 可能会导致问题。

【讨论】:

请记住,Flush() 不是“这就是结束”。这只是“到目前为止刷新所有内容”。您可能想要“这是结束”的原因是让客户端知道它拥有所有内容,而服务器可以去做其他事情 - 更新日志文件,查询数据库计数器或其他任何事情。如果您调用 Response.Flush 然后执行其中一项操作,客户端可能会继续等待更多。如果您调用 Response.End() 则控制跳出并且数据库不会得到查询等。 您也可以使用override Response.Redirect("....", true),其中bool是'endResponse:指示页面的当前执行是否应该终止" 最好使用表单身份验证框架来保护本应通过登录凭据保护的页面。 实际上,为了纠正自己,我相信 Response.Redirect 和 Server.Transfer 的默认值是在内部调用 Response.End ,除非你调用覆盖并传入'false'。你的代码的方式是写 Response.End 永远不会被调用, Response.end 在 .net 中的工作方式与在经典 ASP 中的工作方式大不相同。在 .net 中,它会导致线程异常接收,这可能非常讨厌。【参考方案8】:

我只使用了 Response.End() 作为测试/调试机制

<snip>
Response.Write("myVariable: " + myVariable.ToString());
Response.End();
<snip>

从您在研究方面发布的内容来看,如果需要 Response.End,我会说这将是一个糟糕的设计

【讨论】:

【参考方案9】:

在经典的 asp 上,我在一些 ajax 调用上的 TTFB(到第一个字节的时间)为 3 到 10 秒,比具有更多 SQL 调用的常规页面上的 TTFB 大得多。

返回的 ajax 是要注入页面的一段 HTML。

TTFB 比渲染时间长几秒。

如果我在渲染后添加了一个response.end,TTFB就大大降低了。

我可以通过发出“

以上是关于Response.End() 被认为是有害的吗?的主要内容,如果未能解决你的问题,请参考以下文章

ios 阻止本地通知是不是被认为是有害的?

显式的serialVersionUID 被认为是有害的?

Rspec/Capybara 正在加载,循环要求被认为是有害的

PHP忽略从被认为有害的函数返回的引用?

本地存储可以被认为是安全的吗? [关闭]

Response.End方法