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


198

这篇知识库文章说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();
        }
    }
}

这对我来说似乎很严厉。如KB文章所述,应用程序中的任何后续代码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管道跳到该EndRequest事件HttpApplication。因此,如果您致电CompleteRequest,然后向响应中写入更多内容,则写入内容将发送给客户端。

编辑-2011年4月13日

进一步明晰,请访问:
- 有用的帖子在MSDN博客
- 由乔恩·里德有用的分析


2
不知道自从这个答案以来发生了什么变化,但是我Response.End ThreadAbortException很好。
马斯洛2014年

1
还请记住,Response.Redirect并且Server.Transfer两者都Response.End应该避免。
Owen Blacker

Answers:


66

如果您在应用程序上使用了异常记录器,那么ThreadAbortException这些良性Response.End()调用中的将会使异常记录器被淹没。我认为这是微软所说的“敲门!”。

我只会Response.End()在有一些特殊情况并且无法采取其他措施的情况下使用。也许然后,记录此异常可能实际上指示警告。


107

TL; DR

最初,我建议您仅将对[Response.End]的所有调用替换为[...] CompleteRequest()调用,但是如果要避免回发处理和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。请记住,由于HttpApplication.CompleteRequest跳过了IIS / ASP.NET应用程序管道的其余部分,而不是ASP.NET Page管道(这是应用程序管道的一个阶段),因此必须手动跳过ASP.NET呈现。


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


参考

HttpApplication.CompleteRequest

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

响应结束

提供此方法仅是为了与ASP兼容,也就是说,与ASP.NET之前的ASP.NET之前基于COM的Web编程技术兼容。[强调已添加]

响应。关闭

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


4
>请记住,由于HttpApplication.CompleteRequest跳过了IIS / ASP.NET应用程序管道的其余部分,而不是ASP.NET Page管道(这是应用程序管道的一个阶段),因此必须手动跳过ASP.NET呈现。您如何做到这一点?
PilotBob 2012年

1
请参阅代码的链接,其中Jon Reid演示了如何设置标志以及重写Page的RaisePostBackEvent和Render方法以在需要时跳过常规实现。(你可能做到这一点在基类中的所有应用的页面应该继承。)web.archive.org/web/20101224113858/http://www.c6software.com/...
user423430

4
只是重申一下:HttpApplication.CompleteRequest不会像Response.End那样终止响应。
Glen Little

2
HttpApplication.CompleteRequest也不会停止代码流,因此后续行将继续运行。这可能不会影响浏览器的显示效果,但是如果这些行进行了其他处理,则可能会造成混乱。
约书亚·弗兰克

3
我不能认为,但是Web窗体是设计使然。还有什么会降低性能,调用Response.End()或让页面加载所有内容然后抑制响应?我看不到Response.End()在这里“更多”有害。此外,从以下代码可以明显看出,Microsoft将`ThreadAbortedException'视为正常事件:referencesource.microsoft.com/#System.Web/UI/Page.cs,4875与Response.End()相对的一件事是它可能会失败中止响应,这有时可能会导致显示响应。
加桑2015年

99

这个问题出现在所有Google搜索的关于response.end的信息的顶部附近,所以对于像我这样希望在不渲染整个ASPX页面的情况下响应事件而发布CSV / XML / PDF等的其他搜索,这就是我的处理方式。(对于这样一个简单的任务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()

1
您不应该使用APSX页面来执行此操作。这浪费了很多精力。您应该使用ASMX或Web服务,但不能使用ASPX页面。
mattmanser 2013年

19
这似乎是最简单实现的答案。关键是Response.SuppressContent = True。
克里斯·韦伯

3
@mattmanser-为同一资源的不同表示形式提供单独的页面并不总是容易/最佳/建议的。考虑一下REST等。如果客户端通过标头或参数指示他们想要csv,xml,则此方法当然是最好的,同时仍通过asp.net的常规呈现功能提供html支持。
克里斯·韦伯

1
这对我不起作用。我有一个使用Response.End()的页面,但是使用了Response.Close(),Response.Flush()的各种组合。如果我在Response上具有GzipStream过滤器,则HttpContext.Current.ApplicationInstance.CompleteRequest()和其他各种方法均无法正常工作。似乎正在发生的情况是该页面仍与我的文件一起输出。我终于覆盖了Render()函数(为空白),这为我解决了。
Aerik

1
CompleteRequest跳过了部分应用程序管道,但仍将在页面呈现过程的其余部分中运行,它不是像response.end这样的立即停止,而是更为优雅。有关为何在此页面上的其他答案中有更多的深入说明。
杰·泽洛斯

11

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

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

请参阅以下文章,以了解有关此案例的详细信息。

请注意,即使在调用CompleteRequest()之后,某些文本(例如,从ASPX代码中删除)也将附加到响应输出流中。您可以通过重写Render和RaisePostBackEvent方法(如以下文章中所述)来防止此情况。

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


正如您所描述的,我有兴趣重写Render,但是指向“后续文章”的链接已失效。也许您可以更新您的条目?
paqogomez 2014年

对不起,我迟到了。我不完全记得那篇文章的内容。但是,我已经在webarchive.org上找到了它:web.archive.org/web/20101224113858/http
//www.c6software.com/…– JanŠotola2014年

9

我不同意“ Response.End是有害的 ” 的说法。绝对没有害处。Response.End会说的话;它结束了页面的执行。使用反射器查看其实现方式仅应被视为具有指导意义。


我的2cent Recommendation
AVOID使用Response.End()作为控制流。如果您需要停止请求执行并且意识到(通常)*没有代码将在该点之后执行,请
务必使用Response.End()


* Response.End()ThreadAbortException

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

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

若要查看如何编写必须处理ThreadAbortExceptions的代码,请参阅@Mehrdad对SO的答复如何在他引用RuntimeHelpers.ExecuteCodeWithGuaranteedCleanup方法约束执行区域的finally块检测线程吸收


提到的Rick Strahl文章很有启发性,请务必阅读评论。请注意,Strahl的问题是特定的。他想将数据发送到客户端(图像),然后处理命中跟踪数据库更新,这不会减慢图像的投放速度,这使他成为在调用Response.End之后要做某事的问题。


我们看到了这篇文章stackoverflow.com/questions/16731745/…建议使用Response.SuppressContent = True HttpContext.Current.ApplicationInstance.CompleteRequest()而不是Response.End()
jazzBox 2009年

3

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

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

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


1
我知道需要API说“响应已完成”。但是Response.End()也会使线程中止。这是问题的症结所在。何时将这两件事结合在一起是一个好主意?
Cheeso

2

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

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

当向用户提供文件时,我会使用“刷新”功能,最终可能会导致问题。


请记住,Flush()不是“这就是终点”。只是“到目前为止清除所有内容”。您可能想要“这就是终点”的原因是让客户端知道它具有所有内容,而服务器可以执行其他操作-更新日志文件,查询数据库计数器或执行其他操作。如果您调用Response.Flush,然后执行其中之一,则客户端可能会继续等待更多。如果你到Response.End调用(),然后控制跳出和DB没有得到查询等
Cheeso

您也可以使用重写Response.Redirect(“ ....”,true),其中布尔值为'endResponse:指示当前页面执行是否应终止”
Robert Paulson

最好使用表单身份验证框架来保护本应由登录凭据保护的页面。
罗伯特·保尔森

3
实际上,要纠正自己,我相信Response.Redirect和Server.Transfer的默认值是在内部调用Response.End,除非您调用重写并传递“ false”。编写代码的方式永远不会调用Response.End ,
罗伯特·保尔森

1
.net中的Response.end与经典ASP中的工作有很大不同。在.net中,它会导致线程异常接收,这可能非常讨厌。
安迪

2

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

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

从您发表的研究成果来看,如果需要Response.End,那将是一个糟糕的设计。


0

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

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

TTFB比渲染时间长几秒钟。

如果在渲染之后添加了response.end,则TTFB会大大减少。

我可以通过发出“ </ body> </ html>”来获得相同的效果,但是在输出json或xml时这可能不起作用。这里需要response.end。


我知道这是一个旧答案,但是再说一遍,经典asp是旧的。我发现它很有用;-)
Leif Neland
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.