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

Che*_*eso 197 .net asp.net

这篇知识库文章说,ASP.NET Response.End()中止了一个帖子.

反射器显示它看起来像这样:

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();
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

这对我来说似乎很苛刻.正如知识库文章所说,以下应用程序中的任何代码Response.End()都不会被执行,这违反了最不惊讶的原则.它几乎就像Application.Exit()在WinForms应用程序中.造成线程终止异常Response.End()不开捕,所以代码周围的try...... finally不会满足.

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

任何人都可以建议,我什么时候应该使用Response.End(),何时Response.Close()何地HttpContext.Current.ApplicationInstance.CompleteRequest()

参考:Rick Strahl的博客文章.


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

  • 使用Response.End()作为一个不可捕获抛出,立即终止HttpResponse在特殊的条件.在调试过程中也很有用. 避免Response.End()完成常规反应.
  • 用于Response.Close()立即关闭与客户端的连接.根据此MSDN博客文章,此方法不适用于正常的HTTP请求处理. 你不太可能有充分的理由来调用这种方法.
  • 用于CompleteRequest()结束正常请求. 在当前事件完成后CompleteRequest,ASP.NET管道将跳转到事件.因此,如果您打电话,然后在响应中写入更多内容,则会将写入内容发送给客户端.EndRequestHttpApplicationCompleteRequest

编辑 - 2011年4月13日

可在此处进一步了解:
- MSDN博客上的有用帖子
- Jon Reid的有用分析

use*_*430 106

TL; DR

最初我建议您只需用[...] CompleteRequest()调用替换所有对[Response.End]的调用,但是如果你想避免回发处理和html渲染,你需要添加[.. .]也覆盖.

Jon Reid,"最终分析"


根据MSDN,Jon Reid和Alain Renon:

ASP.NET性能 - 异常管理 - 编写避免异常的代码

Server.Transfer,Response.Redirect,Response.End方法都引发异常.这些方法中的每一个都在内部调用Response.End.反过来,对Response.End的调用会导致ThreadAbortException异常.

ThreadAbortException解决方案

HttpApplication.CompleteRequest()设置一个变量,该变量使线程跳过HttpApplication事件管道[ - ]中的大多数事件而不是Page事件链而不是Application事件链.

...

创建一个类级别变量,标记页面是否应终止,然后在处理事件或呈现页面之前检查变量.[...]我建议只重写RaisePostBackEvent和Render方法

当性能很重要时,Response.End和Response.Close不用于正常的请求处理.Response.End是一种方便,严厉的终止请求处理的方法,具有相关的性能损失.Response.Close用于在IIS /套接字级别立即终止HTTP响应,并导致KeepAlive等问题.

结束ASP.NET请求的推荐方法是HttpApplication.CompleteRequest.请记住,必须手动跳过ASP.NET呈现,因为HttpApplication.CompleteRequest会跳过IIS/ASP.NET应用程序管道的其余部分,而不是ASP.NET页面管道(这是应用程序管道中的一个阶段).


版权所有©2001-2007,C6 Software,Inc尽我所知.


参考

HttpApplication.CompleteRequest

导致ASP.NET绕过所有事件并在HTTP管道执行链中进行过滤,并直接执行EndRequest事件.

到Response.End

此方法仅用于与ASP兼容,即与ASP.NET.preceded ASP.NET之前的基于COM的Web编程技术兼容.[强调补充]

Response.Close

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

  • >请记住,必须手动跳过ASP.NET呈现,因为HttpApplication.CompleteRequest会跳过IIS/ASP.NET应用程序管道的其余部分,而不是ASP.NET页面管道(这是应用程序管道中的一个阶段).你是如何做到这一点的? (4认同)
  • 只是重述:HttpApplication.CompleteRequest不会像Response.End那样终止响应. (4认同)
  • 我无法思考,但Web表单已被设计破坏.什么是性能降级,调用Response.End()或让页面加载所有内容然后抑制响应?我无法看到Response.End()在哪里"更有害".此外,Microsoft将"ThreadAbortedException"视为正常事件,如以下代码所示:http://referencesource.microsoft.com/#System.Web/UI/Page.cs,4875反对Response.End()的一件事是它可能无法中止响应,这可能偶尔会在显示的响应中导致. (3认同)
  • HttpApplication.CompleteRequest也不会停止代码流,因此后续行继续运行.这可能不会影响浏览器看到的内容,但如果这些行进行任何其他处理,则可能会让人感到困惑. (2认同)

Jay*_*los 96

这个问题出现在所有谷歌搜索的顶部附近有关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()
Run Code Online (Sandbox Code Playgroud)

  • 这似乎是最简单实现的答案.关键是Response.SuppressContent = True. (17认同)
  • @mattmanser - 为同一资源的不同表示创建一个单独的页面并不总是容易/最好/可取的.想想REST等等.如果客户端通过标头或参数指示他们想要csv,xml,这种方法肯定是最好的,同时仍然通过asp.net的普通渲染工具提供html支持. (3认同)

spo*_*son 64

如果您在应用程序上使用了异常记录器,则会使用ThreadAbortException这些良性Response.End()呼叫中的s 来淡化它.我认为这是微软的说法"敲掉它!".

我只会使用,Response.End()如果有一些特殊情况,没有其他行动是可能的.也许那时,记录此异常可能实际上表示警告.


Jan*_*ola 11

关于"我仍然不知道Response.Close和CompleteRequest()之间的区别"我会说:

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

有关此案例的完善摘要,请参阅以下文章.

请注意,即使在调用CompleteRequest()之后,某些文本(例如,从ASPX代码重新编译)也会附加到响应输出流中.您可以通过重写Render和RaisePostBackEvent方法来阻止它,如下文所述.

BTW:我同意阻止使用Response.End(),特别是在将数据写入http流以模拟文件下载时.我们过去曾使用过Response.End(),直到我们的日志文件充满了ThreadAbortExceptions.


Rob*_*son 9

我不同意声明" Response.End是有害的 ".这绝对没有害处.Response.End做它说的话; 它结束了页面的执行.使用反射器来查看它的实现方式只应被视为具有指导意义.


我的2cent建议使用作为控制流程
避免Response.End().
不要使用Response.End(),如果你需要停止执行的请求,并注意(典型值)*无码将执行过去的那个点.


*Response.End()ThreadAbortException.

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

ThreadAbortException是一个可以捕获的特殊异常,但它会在catch块的末尾自动再次引发.

要了解如何编写必须处理ThreadAbortExceptions的代码,请参阅@ Mehrdad对SO的回复如何在finally块检测一个threadabortexception,其中他引用了RuntimeHelpers.ExecuteCodeWithGuaranteedCleanup方法约束执行区域


提到的Rick Strahl文章很有启发性,请务必阅读评论.请注意,Strahl的问题是具体的.他希望将数据提供给客户端(图像),然后处理命中跟踪数据库更新,这不会减慢图像的服务速度,这使他在调用Response.End后做了一些事情.