asp.net mvc源码分析-路由篇 如何找到 IHttpHandler
学习是使用asp.net已经有很长一段时间了,现在就来分析一下mvc的整过过程吧。个人计划写一个mvc系列的博文,仅从源代码的角度来分析mvc。在接触mvc时我们一定会经历路由,那么路由这东东是怎么搞出来的啊。在我们的web.config中有这么一句: <add assembly="System.Web.Routing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35" /> 看来路由是它咋负责了。在这个dll中有一个很特殊的类UrlRoutingModule
我们来看看它里面主要的核心代码吧:
protected virtual void Init(HttpApplication application) { if (application.Context.Items[_contextKey] == null ) { application.Context.Items[_contextKey] = _contextKey; application.PostResolveRequestCache += new EventHandler( this .OnApplicationPostResolveRequestCache); } } private void OnApplicationPostResolveRequestCache( object sender, EventArgs e) { HttpContextBase context = new HttpContextWrapper(((HttpApplication) sender).Context); this .PostResolveRequestCache(context); } public virtual void PostResolveRequestCache(HttpContextBase context) { RouteData routeData = this .RouteCollection.GetRouteData(context); if (routeData != null ) { IRouteHandler routeHandler = routeData.RouteHandler; if (routeHandler == null ) { throw new InvalidOperationException( string .Format(CultureInfo.CurrentUICulture, SR.GetString( "UrlRoutingModule_NoRouteHandler" ), new object [0])); } if (!(routeHandler is StopRoutingHandler)) { RequestContext requestContext = new RequestContext(context, routeData); context.Request.RequestContext = requestContext; IHttpHandler httpHandler = routeHandler.GetHttpHandler(requestContext); if (httpHandler == null ) { throw new InvalidOperationException( string .Format(CultureInfo.CurrentUICulture, SR.GetString( "UrlRoutingModule_NoHttpHandler" ), new object [] { routeHandler.GetType() })); } if (httpHandler is UrlAuthFailureHandler) { if (!FormsAuthenticationModule.FormsAuthRequired) { throw new HttpException(0x191, SR.GetString( "Assess_Denied_Description3" )); } UrlAuthorizationModule.ReportUrlAuthorizationFailure(HttpContext.Current, this ); } else { context.RemapHandler(httpHandler); } } } } |
在IHttpModule.Init中注册了一个PostResolveRequestCache事件,而该事件主要是调用PostResolveRequestCache这个方法,在这个方法里面有几句很重要的代码是
RouteData routeData = this.RouteCollection.GetRouteData(context);
IRouteHandler routeHandler = routeData.RouteHandler;
RequestContext requestContext = new RequestContext(context, routeData);
context.Request.RequestContext = requestContext;
IHttpHandler httpHandler = routeHandler.GetHttpHandler(requestContext);
context.RemapHandler(httpHandler);
让我们来分析第一句RouteData routeData = this.RouteCollection.GetRouteData(context) ,这句我们猜测是获取路由信息。要想理解这句代码又得回到我们程序中来,我们在Global.asax.cs文件中的RegisterRoutes方法中,默认有这么一句
routes.MapRoute(
"Default", // 路由名称
"{controller}/{action}/{id}", // 带有参数的 URL
new { controller = "Home", action = "Index", id = UrlParameter.Optional } // 参数默认值
);
这句代码主要是注册一个路由,这里的url要注意不能随便写,需要有controller和action。具体是怎么实现的了?
public static Route MapRoute( this RouteCollection routes, string name, string url, object defaults, object constraints, string [] namespaces) { Route route = new Route(url, new MvcRouteHandler()) { Defaults = new RouteValueDictionary(defaults), Constraints = new RouteValueDictionary(constraints), DataTokens = new RouteValueDictionary() }; if ((namespaces != null ) && (namespaces.Length > 0)) { route.DataTokens[ "Namespaces" ] = namespaces; } routes.Add(name, route); return route; } |
各参数如下
routeName="Default", // 路由名称
routeUrl= "{controller}/{action}/{id}", // 带有参数的 URL
defaults=new { controller = "Home", action = "Index", id = UrlParameter.Optional } // 参数默认值
constraints=null
namespaces=null
在这里创建了一个Route实例并且把它加入到RouteCollection中了。
如果我们的项目中有特殊的需要,需要创建自己的HttpHandler这么办了?
其实很简单只要我们注册自己的IRouteHandler了,routes.Add(new Route("{controller}/{action}/{id}",new MvcRouteHandler())); 然后在里面GetHttpHandler实现自己的逻辑处理
现在又让我们回到 RouteData routeData = this.RouteCollection.GetRouteData(context);这句代码中来,GetRouteData的主要代码如下:
public RouteData GetRouteData(HttpContextBase httpContext)
{
using (this.GetReadLock())
{
foreach (RouteBase base2 in this)
{
RouteData routeData = base2.GetRouteData(httpContext);
if (routeData != null)
{
return routeData;
}
}
}
return null;
}
在这里的base2就是我们先前调用MapRoute是添加的Route的。而Route的GetRouteData的方法如下:
public override RouteData GetRouteData(HttpContextBase httpContext) { string virtualPath = httpContext.Request.AppRelativeCurrentExecutionFilePath.Substring(2) + httpContext.Request.PathInfo; RouteValueDictionary values = this ._parsedRoute.Match(virtualPath, this .Defaults); if (values == null ) { return null ; } RouteData data = new RouteData( this , this .RouteHandler); if (! this .ProcessConstraints(httpContext, values, RouteDirection.IncomingRequest)) { return null ; } foreach (KeyValuePair< string , object > pair in values) { data.Values.Add(pair.Key, pair.Value); } if ( this .DataTokens != null ) { foreach (KeyValuePair< string , object > pair2 in this .DataTokens) { data.DataTokens[pair2.Key] = pair2.Value; } } return data; } |
这个方法很复杂,有许多验证和检查,我们主要关心一句 RouteData data = new RouteData(this, this.RouteHandler);
当然剩下 RequestContext requestContext = new RequestContext(context, routeData);
context.Request.RequestContext = requestContext;这2句没什么特别了。
现在让我们来看看IHttpHandler httpHandler = routeHandler.GetHttpHandler(requestContext);这句究竟干了些什么,意思很明白获取Httphandler。
那么MvcRouteHandler是如何获取一个Httphandler的了,
protected virtual IHttpHandler GetHttpHandler(RequestContext requestContext) {
requestContext.HttpContext.SetSessionStateBehavior(GetSessionStateBehavior(requestContext));
return new MvcHandler(requestContext);
}
直接返回了一个MvcHandler实例。
最有一句context.RemapHandler(httpHandler); 很简单很好明白吧,在HttpContext的RemapHandler方法中有这么一句 this._remapHandler = handler;
在HttpContext中有这个属性
internal IHttpHandler RemapHandlerInstance
{
get
{
return this._remapHandler;
}
}
那么这个东西又是什么时候调用的了,在HttpApplication的内部类MaterializeHandlerExecutionStep中的 void HttpApplication.IExecutionStep.Execute()方法调用
if (httpContext.RemapHandlerInstance != null)
{
httpContext.Handler = httpContext.RemapHandlerInstance;
}
看到MaterializeHandlerExecutionStep这个了类名,我想大家都能猜到吧。在内部类PipelineStepManager中BuildSteps方法有
HttpApplication.IExecutionStep step = new HttpApplication.MaterializeHandlerExecutionStep(app);
app.AddEventMapping("ManagedPipelineHandler", RequestNotification.MapRequestHandler, false, step);
我想大家看到这里对mvc整个路由应该有个大致的理解了吧。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· 阿里巴巴 QwQ-32B真的超越了 DeepSeek R-1吗?
· 【译】Visual Studio 中新的强大生产力特性
· 10年+ .NET Coder 心语 ── 封装的思维:从隐藏、稳定开始理解其本质意义
· 【设计模式】告别冗长if-else语句:使用策略模式优化代码结构