博客后台切换至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打开DotNetReferenceSource\Source\ndp.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(),所以不会遇到这个问题。
更多精彩内容:http://www.bianceng.cnhttp://www.bianceng.cn/webkf/aspx/
以上是小编为您精心准备的的内容,在的博客、问答、公众号、人物、课程等栏目也有的相关内容,欢迎继续使用右上角搜索按钮进行搜索源代码
, string
, redirect
, context
, 代码
, response
, 重定向redirect参数mvc
Request.RawUrl
java 重定向 redirect、js redirect 重定向、重定向redirect、html redirect 重定向、两次重定向redirect,以便于您获取更多的相关知识。