zoukankan      html  css  js  c++  java
  • MVC源码分析

    上一篇 说到了路由事件注册以及路由表的生成, 前面 也解析到了, 管道事件的建立, 那么接下来, 肯定就是要调用执行这些事件了, 这些就不表了, 我已经得到我想要的部分了, 接下来, 在执行这些管道事件的时候, 肯定就会执行到之前 UrlRoutingModule注册的方法. 接下来, 就看一下, 这个事件干了些什么. 

    一、OnApplicationPostResolveRequestCache 方法

    private void OnApplicationPostResolveRequestCache(object sender, EventArgs e)
    {
        HttpApplication application = (HttpApplication) sender;
        HttpContextBase context = new HttpContextWrapper(application.Context);
        this.PostResolveRequestCache(context);
    }

    这里调用了自己(UrlRoutingModule)的 PostResolveRequestCache 方法. 

    public virtual void PostResolveRequestCache(HttpContextBase context)
    {
       //根据http上下文去 之前生成的路由表中查找匹配的路由 RouteData routeData = this.RouteCollection.GetRouteData(context); if (routeData != null) {
         //这里返回的 IRouteHandler 其实就是 MvcRouteHandler(上一篇)有提到过
         //这个MvcRouteHandler中, 就有一个方法, 用来返回 IHttpHander的, 其实就是返回的 MvcHandler
    IRouteHandler routeHandler = routeData.RouteHandler; if (routeHandler == null) { throw new InvalidOperationException(string.Format(CultureInfo.CurrentCulture,
               SR.GetString("UrlRoutingModule_NoRouteHandler"), new object[0])); }
         //在注册路由的时候, Ignore方式注册的, 会返回StopRoutingHandler if (!(routeHandler is StopRoutingHandler)) {
           //在这里, 首先创建了请求上下文 RequestContext requestContext = new RequestContext(context, routeData); context.Request.RequestContext = requestContext;
            //在这里获取到MVC的处理接口, 就是 MvcHandler 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 {
              //将 MvcHandler 存入 HttpContext 对象的 _remapHandler 属性中 context.RemapHandler(httpHandler); } } } }

     按照顺序, 看上面我标红的接个方法.

    1. GetRouteData()方法

    //RouteCollection
    public
    RouteData GetRouteData(HttpContextBase httpContext) { if (httpContext == null) { throw new ArgumentNullException("httpContext"); } if (httpContext.Request == null) { throw new ArgumentException(SR.GetString("RouteTable_ContextMissingRequest"), "httpContext"); } if (base.Count != 0) { bool flag = false; bool flag2 = false; if (!this.RouteExistingFiles) { flag = this.IsRouteToExistingFile(httpContext); flag2 = true; if (flag) { return null; } } using (this.GetReadLock()) { foreach (RouteBase base2 in this) { RouteData routeData = base2.GetRouteData(httpContext); if (routeData != null) { if (!base2.RouteExistingFiles) { if (!flag2) { flag = this.IsRouteToExistingFile(httpContext); flag2 = true; } if (flag) { return null; } } return routeData; } } } } return null; }

    接着看, 这里调用了 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;
    }

    这里就会根据之前注册路由的限制条件去匹配路由了. 将最后匹配到的那个路由返回.

    2. GetHttpHandler()方法

    这里会调用 MvcRouteHandler 的 GetHttpHandler 方法, 前面篇章提过的.

    protected virtual IHttpHandler GetHttpHandler(RequestContext requestContext)
    {
        requestContext.HttpContext.SetSessionStateBehavior(this.GetSessionStateBehavior(requestContext));
        return new MvcHandler(requestContext);
    }

    返回一个 MvcHandler .

    3. RemapHandler()方法

    这里会调用 HttpContext 的RemapHandler 方法.

    public void RemapHandler(IHttpHandler handler)
    {
        this.EnsureHasNotTransitionedToWebSocket();
        IIS7WorkerRequest request = this._wr as IIS7WorkerRequest;
        if (request != null)
        {
            if (this._notificationContext.CurrentNotification >= RequestNotification.MapRequestHandler)
            {
                throw new InvalidOperationException(SR.GetString("Invoke_before_pipeline_event",
             new object[] { "HttpContext.RemapHandler", "HttpApplication.MapRequestHandler" })); } string handlerType = null; string handlerName = null; if (handler != null) { Type type = handler.GetType(); handlerType = type.AssemblyQualifiedName; handlerName = type.FullName; } request.SetRemapHandler(handlerType, handlerName); } this._remapHandler = handler; }

    这里主要就是看最后一句了, 将 mvchandler存入到 HttpContext._remapHandler 中

    到这里, 其实就能看到 路由匹配的过程以及获取到 mvc处理程序(这个是入口). 

    二、路由匹配

    在分析路由的时候, 可以借助一个插件 : RouteDebugger, 获取方式为, 在vs程序包控制台中, 执行以下命令

    PM> Install-Package routedebugger

    添加完之后, 程序会自动在web.config中添加如下配置:

    <add key="RouteDebugger:Enabled" value="true" />

    我们只需要直接运行程序就好了. 先看一张效果图:

    在图上, 能清晰的看到, 匹配过程中, 与其中的哪一个路由能匹配的上. 哪一些不能匹配上.

    两个小问题:

      如果有两个匹配规则相同, 但是路由名不同的, 会匹配上哪一个呢?

        哪一个在前面注册的, 就回匹配到哪一个, 后面的不会继续匹配.

      那会不会有匹配规则不同, 但是路由名相同的呢?

        一般在使用key的时候, 都是要保证key的唯一性, 路由同样如此, 路由名不能重复, 必须唯一, 否则会报错.

    有兴趣的朋友, 可以去了解下

    目录已同步

  • 相关阅读:
    wex5 实战 框架拓展之2 事件派发与data刷新
    wex5 实战 框架拓展之1 公共data组件(Data)
    wex5 实战 HeidiSQL 导入Excel数据
    wex5 实战 手指触屏插件 hammer的集成与优劣
    wex5 实战 登陆帐号更换与用户id一致性
    wex5 实战 用户点评与提交设计技巧
    wex5 实战 省市县三级联动与地址薄同步
    wex5 实战 wex5与js的组件关系与执行顺序(父子与先后)
    wex5 实战 单页模式下的多页面数据同步
    [BZOJ]4237: 稻草人
  • 原文地址:https://www.cnblogs.com/elvinle/p/6278038.html
Copyright © 2011-2022 走看看