Welcome 微信登录

首页 / 网页编程 / ASP.NET / Response.Redirect引起的“无法在发送HTTP标头之后进行重定向”

Response.Redirect引起的“无法在发送HTTP标头之后进行重定向”2015-04-17 cnblogs dudu博客后台切换至i.cnblogs.com之后,在日志中发现大量的“无法在发送HTTP标头之后进行重定向”(Cannot redirect after HTTP headers have been sent)的错误信息。

检查代码发现问题是由下面的代码触发的:

IHttpHandler IHttpHandlerFactory.GetHandler(HttpContext context, string requestType, string url, string pathTranslated){context.Response.Redirect("http://i.cnblogs.com/" + context.Request.RawUrl.Substring(context.Request.RawUrl.LastIndexOf("/") + 1));//后续也有context.Response.Redirect代码//...return PageParser.GetCompiledPageInstance(newurl, path, context);}
“无法在发送HTTP标头之后进行重定向”问题来源于Response.Redirect之后,又进行了Response.Redirect。

解决方法很简单:在Response.Redirect之后立即返回。

IHttpHandler IHttpHandlerFactory.GetHandler(HttpContext context, string requestType, string url, string pathTranslated){context.Response.Redirect("http://i.cnblogs.com/" + context.Request.RawUrl.Substring(context.Request.RawUrl.LastIndexOf("/") + 1));return null;//...}
为什么之前没有加return null呢?因为以前一直以为Response.Redirect会结束当前请求,不会执行Response.Redirect之后的代码。

现在残酷的现实说明了不完全是这样的,那问题背后的真相是什么?让我们来一探究竟。

由于微软公开了.NET Framework的源代码,现在无需再看Reflactor出来的代码,可以直接下载源代码用Visual Studio进行查看。

.NET Framework源代码下载链接:http://referencesource.microsoft.com/download.html (相关新闻:微软开放了.NET 4.5.1的源代码)

用Visual Studio打开DotNetReferenceSourceSource dp.sln,搜索HttpResponse.cs,找到Response.Redirect的实现代码:

public void Redirect(String url){Redirect(url, true, false);}
实际调用的是internal void Redirect(String url, bool endResponse, bool permanent) ,传给endResponse的值的确是true啊,为什么后面的代码还会执行?

进一步查看internal void Redirect()的实现代码(省略了无关代码):

internal void Redirect(String url, bool endResponse, bool permanent) {//...Page page = _context.Handler as Page;if ((page != null) && page.IsCallback) {//抛异常}// ... url处理Clear(); //Clears all headers and content output from the buffer stream.//...this.StatusCode = permanent ? 301 : 302; //进行重定向操作//..._isRequestBeingRedirected = true; var redirectingHandler = Redirecting;if (redirectingHandler != null) {redirectingHandler(this, EventArgs.Empty);}if (endResponse)End(); //结束当前请求}
从上面的代码可以看出,我们要找的真相在End()方法中,继续看HttpResponse.End()的实现代码:
public void End() {if (_context.IsInCancellablePeriod) {AbortCurrentThread();}else {// when cannot abort execution, flush and supress further output_endRequiresObservation = true;if (!_flushing) { // ignore Reponse.End while flushing (in OnPreSendHeaders)Flush();_ended = true;if (_context.ApplicationInstance != null) {_context.ApplicationInstance.CompleteRequest();}}}}
注意啦!真相浮现了!

以前一直以为的Response.Redirect会结束当前请求,就是上面的AbortCurrentThread()情况,如果将Response.Redirect放在try...catch中就会捕捉到ThreadAbortException异常。

通常情况下,我们在WebForms的Page或MVC的Controller中进行Redirect,_context.IsInCancellablePeriod的值为true,执行的是AbortCurrentThread(),所以不会遇到这个问题。