|
马上注册,结交更多好友,享用更多功能,让你轻松玩转社区。
您需要 登录 才可以下载或查看,没有帐号?立即注册
x
我之所以想学。NET,是因为一直觉的BILLGATES好厉害,希望有一天能去微软,虽然现在还距离遥远,呵呵:)博客背景切换至i.cnblogs.com以后,在日记中发明大批的“没法在发送HTTP标头以后举行重定向”(CannotredirectafterHTTPheadershavebeensent)的毛病信息。
反省代码发明成绩是由上面的代码触发的:
- IHttpHandlerIHttpHandlerFactory.GetHandler(HttpContextcontext,stringrequestType,stringurl,stringpathTranslated){context.Response.Redirect("http://i.cnblogs.com/"+context.Request.RawUrl.Substring(context.Request.RawUrl.LastIndexOf("/")+1));//后续也有context.Response.Redirect代码//...returnPageParser.GetCompiledPageInstance(newurl,path,context);}
复制代码
“没法在发送HTTP标头以后举行重定向”成绩来历于Response.Redirect以后,又举行了Response.Redirect。
办理办法很复杂:在Response.Redirect以后当即前往。
- IHttpHandlerIHttpHandlerFactory.GetHandler(HttpContextcontext,stringrequestType,stringurl,stringpathTranslated){context.Response.Redirect("http://i.cnblogs.com/"+context.Request.RawUrl.Substring(context.Request.RawUrl.LastIndexOf("/")+1));returnnull;//...}
复制代码
为何之前没有加returnnull呢?由于之前一向觉得Response.Redirect会停止以后哀求,不会实行Response.Redirect以后的代码。
如今严酷的实际申明了不完整是如许的,那成绩面前的原形是甚么?让我们来一探求竟。
因为微软公然了.NETFramework的源代码,如今无需再看Reflactor出来的代码,能够间接下载源代码用VisualStudio举行检察。
.NETFramework源代码下载链接:http://referencesource.microsoft.com/download.html(相干旧事:微软开放了.NET4.5.1的源代码)
用VisualStudio翻开DotNetReferenceSourceSourcendp.sln,搜刮HttpResponse.cs,找到Response.Redirect的完成代码:- publicvoidRedirect(Stringurl){Redirect(url,true,false);}
复制代码 实践挪用的是internalvoidRedirect(Stringurl,boolendResponse,boolpermanent),传给endResponse的值切实其实是true啊,为何前面的代码还会实行?
进一步检察internalvoidRedirect()的完成代码(省略了有关代码):
- internalvoidRedirect(Stringurl,boolendResponse,boolpermanent){//...Pagepage=_context.HandlerasPage;if((page!=null)&&page.IsCallback){//抛非常}//...url处置Clear();//Clearsallheadersandcontentoutputfromthebufferstream.//...this.StatusCode=permanent?301:302;//举行重定向操纵//..._isRequestBeingRedirected=true;varredirectingHandler=Redirecting;if(redirectingHandler!=null){redirectingHandler(this,EventArgs.Empty);}if(endResponse)End();//停止以后哀求}
复制代码
从下面的代码能够看出,我们要找的原形在End()办法中,持续看HttpResponse.End()的完成代码:
- publicvoidEnd(){if(_context.IsInCancellablePeriod){AbortCurrentThread();}else{//whencannotabortexecution,flushandsupressfurtheroutput_endRequiresObservation=true;if(!_flushing){//ignoreReponse.Endwhileflushing(inOnPreSendHeaders)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(),以是不会碰到这个成绩。
而我们如今的场景恰好是由于_context.IsInCancellablePeriod的值为false,为何会是false呢?
进一步看一下_context.IsInCancellablePeriod的完成:- privateint_timeoutState;//0=non-cancelable,1=cancelable,-1=canceledinternalboolIsInCancellablePeriod{get{return(Volatile.Read(ref_timeoutState)==1);}}
复制代码 依据下面的代码,触发这个成绩的前提是_timeoutState的值要末是0,要末是-1,依据我们的实践情形,应当是0=non-cancelable。
再来看看我们的实践使用场景,我们是在完成IHttpHandlerFactory接口的GetHandler办法中举行Response.Redirect操纵的,也就是说在这个阶段_timeoutState的值还没被设置(默许值就是0)。为了考证这个设法,持续看一下_timeoutState在哪一个阶段设值的。
Shift+F12找到一切援用_timeoutState的中央,在HttpConext中发明了设置_timeoutState的办法BeginCancellablePeriod,完成代码以下:
- internalvoidBeginCancellablePeriod(){//ItcouldbecausedbyanexceptioninOnThreadStartif(Volatile.Read(ref_timeoutStartTimeUtcTicks)==-1){SetStartTime();}Volatile.Write(ref_timeoutState,1);}
复制代码
然后再Shift+F12找到了在HttpApplication.ExecuteStep()中挪用了BeginCancellablePeriod():
- internalExceptionExecuteStep(IExecutionStepstep,refboolcompletedSynchronously){//..if(step.IsCancellable){_context.BeginCancellablePeriod();//requestcanbecancelledfromthispoint}//..}
复制代码
从下面的代码能够看出,当step.IsCancellable为true时,会挪用BeginCancellablePeriod(),就不会呈现这个成绩。
而我们用到的IHttpHandlerFactory.GetHandler()地点的IExecutionStep的完成大概将IsCancellable设置为了false。
那IHttpHandlerFactory.GetHandler()是在哪一个IExecutionStep的完成中挪用的呢?
在园子里木宛城主的一篇写得十分棒的博文(ASP.NET那点不为人知的事)中找到了谜底——MapHandlerExecutionStep:当实行到MapHandlerExecutionStep时会实行以下代码猎取终极实行哀求:context.Handler=this._application.MapHttpHandler()。HttpApplication对象的MapHttpHandler办法将依据设置文件分离哀求范例和URL以挪用响应的IHttpHandlerFactory来猎取HttpHandler对象。 我们再回到.NETFramework的源代码中看一看MapHandlerExecutionStep的完成:
- //executionstep--mapHTTPhandler(usedtobeaseparatemodule)internalclassMapHandlerExecutionStep:IExecutionStep{privateHttpApplication_application;internalMapHandlerExecutionStep(HttpApplicationapp){_application=app;}voidIExecutionStep.Execute(){//...}boolIExecutionStep.CompletedSynchronously{get{returntrue;}}boolIExecutionStep.IsCancellable{get{returnfalse;}}}
复制代码
看到有无?IExecutionStep.IsCancellable前往的值是false。
到此,内情毕露,水落石出!
请看年夜屏幕——
因为MapHandlerExecutionStep(挪用IHttpHandlerFactory.GetHandler()的中央)前往的IsCancellable的值是false,因而在HttpApplication.ExecuteStep()实行时没有挪用_context.BeginCancellablePeriod()——也就是没有把_timeoutState设置为1,_context.IsInCancellablePeriod的值就是false。从而形成在Response.Redirect中举行Response.End()时没有实行AbortCurrentThread()(一般情形下城市实行这个)。因而代码持续实行,前面又来一次Response.Redirect,终极激发了——“没法在发送HTTP标头以后举行重定向”(CannotredirectafterHTTPheadershavebeensent)。
听03很多师兄说主讲老师杭城方讲课很差就连旁听也没有去了) |
|