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.Redirect
和 Server.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()
调用的 ThreadAbortException
s 淡化。我认为这是微软的表达方式“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() 被认为是有害的吗?的主要内容,如果未能解决你的问题,请参考以下文章