MVC 路由源码解析

//到页面底部下载源,配合效果跟好。  public class MvcApplication : System.Web.HttpApplication
    {
        protected void Application_Start()
        {

            RouteConfig.RegisterRoutes(RouteTable.Routes);
            //调用RouteConfig类的RegisterRoutes方法注册路由
            //RouteTable.Routes是一个储存Route的集合
        }
    }

我们转到RouteConfig类的定义

    public class RouteConfig
    {
        public static void RegisterRoutes(RouteCollection routes)
        {
            routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
            //忽略可用的URL
           // 例如: routes.IgnoreRoute("Home/Index");
            //在浏览器输入 http://localhost:47662/Home/Index
            //得到:HTTP 错误 404.0 - Not Found 的提示

            //默认值
             var defaults = new RouteValueDictionary { { "controller", "Home" }, { "action","Index" } };

             //路由对象
               Route R = new Route("{controller}/{action}/{*id}", null, null, null, new MvcRouteHandler());
            //加入集合
            RouteTable.Routes.Add("Default",R);

        }
    }

转到:Route 类去看怎么初始化构函数的

namespace MyRotue
{
    //这个类不是系统的,是自己写的
    public class Route : RouteBase
    {
        private ParsedRoute _parsedRoute;
        private string _url;

        public Route(string url, IRouteHandler routeHandler)
        {
            this.Url = url;
            this.RouteHandler = routeHandler;
        }

        public Route(string url, RouteValueDictionary defaults, IRouteHandler routeHandler)
        {
            this.Url = url;
            this.Defaults = defaults;
            this.RouteHandler = routeHandler;
        }

        public Route(string url, RouteValueDictionary defaults, RouteValueDictionary constraints, IRouteHandler routeHandler)
        {
            this.Url = url;
            this.Defaults = defaults;
            this.Constraints = constraints;
            this.RouteHandler = routeHandler;
        }

        public Route(string url, RouteValueDictionary defaults, RouteValueDictionary constraints, RouteValueDictionary dataTokens, IRouteHandler routeHandler)
        {
            this.Url = url; //URL模板
            this.Defaults = defaults; //默认值
            this.Constraints = constraints; //约束
            this.DataTokens = dataTokens; //获取或设置传递到路由处理程序但未用于确定该路由是否匹配 URL 模式的自定义值。
            this.RouteHandler = routeHandler; //路由处理请求的对象

            //当初始化 this.Url 模板时:将给Url赋值

        }

        public string Url
        {
            get
            {
                return (this._url ?? string.Empty);
            }
            set
            {
                //this._parsedRoute:解析完的路由,把模板传入RouteParser(路由解析器)类的Parse()方法中,
                //然后返回解析好了的路由。
                this._parsedRoute = RouteParser.Parse(value);
                this._url = value;
            }
        }// 这不是完全代码目前还用不到下面的方法,就先不贴出来了
}
我们转到RouteParser.Parse(Value)中出看下怎么解析的
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;

namespace MyRotue
{
    public class RouteParser
    {

        public static ParsedRoute Parse(string routeUrl)
        {
           // -------------------------------
            // 这是第一步,先进入这个方法

            //判断routeUrl 是否为空
            if (routeUrl == null)
            {
                routeUrl = string.Empty;
            }

              //判断是不是无效路由
            if (IsInvalidRouteUrl(routeUrl))
            {
                throw new ArgumentException("出错了");
            }

            //拆分URL路径,如果routeUrl:Home/Index/Id
            //将被拆分成:[Home],[/],[Index],[/],[Index] 这样的数组
            IList<string> pathSegments = SplitUrlToPathSegmentStrings(routeUrl);

            //验证URL部分
            Exception exception = ValidateUrlParts(pathSegments);

            if (exception != null)
            {
                throw exception;
            }
            //分类
            return new ParsedRoute(SplitUrlToPathSegments(pathSegments));
        }

//这不是全部代码}

我们转到IsInvalidRouteUrl(routeUrl)这个方法,看到是怎么判断的
internal static bool IsInvalidRouteUrl(string routeUrl)
{

    //检查开头字符串,是否有 "~" 和  "/"
    if (!routeUrl.StartsWith("~", StringComparison.Ordinal) && !routeUrl.StartsWith("/", StringComparison.Ordinal))
    {
        //在判断字符串有没有 "?"
        return (routeUrl.IndexOf(‘?‘) != -1);
    }
    return true;
}

转到:SplitUrlToPathSegmentStrings()这个方法

      internal static IList<string> SplitUrlToPathSegmentStrings(string url)
        {
            List<string> list = new List<string>();
            if (!string.IsNullOrEmpty(url))
            {
                int index;
                for (int i = 0; i < url.Length; i = index + 1)
                {
                    //获取"/" 在 url中的位置
                    index = url.IndexOf(‘/‘, i);

                    if (index == -1)
                    {
                       // 获取最后一个参数
                        string str = url.Substring(i);
                        if (str.Length > 0)
                        {
                            list.Add(str);
                        }
                        return list;
                    }
                    //获取参数
                    string item = url.Substring(i, index - i);
                    if (item.Length > 0)
                    {
                        list.Add(item);
                    }
                    list.Add("/");
                }
            }
            return list;
        }

转到 ValidateUrlParts()方法

    private static Exception ValidateUrlParts(IList<string> pathSegments)
        {
            //使用参数名称
            HashSet<string> usedParameterNames = new HashSet<string>(StringComparer.OrdinalIgnoreCase);
            bool? nullable = null;

            foreach (string str in pathSegments)
            {
                bool flag2;
                if (!nullable.HasValue)
                {
                                        //判断是不是分割符
                    nullable = new bool?(IsSeparator(str));
                    flag2 = nullable.Value;
                }
                else
                {

                    flag2 = IsSeparator(str);
                    //如果连续有两个"/"就执行异常
                    if (flag2 && nullable.Value)
                    {
                        return new ArgumentException("出错了");
                    }
                    nullable = new bool?(flag2);
                }
                if (!flag2)
                {
                    Exception exception;
                                                            //解析URL分类
                    IList<PathSubsegment> pathSubsegments = ParseUrlSegment(str, out exception);
                    if (exception != null)
                    {
                        return exception;
                    }

                    //验证URL分类
                    exception = ValidateUrlSegment(pathSubsegments, usedParameterNames, str);
                    if (exception != null)
                    {
                        return exception;
                    }

                }
            }
            return null;
        }转到:IsSeparator
 internal static bool IsSeparator(string s)
        {
            return string.Equals(s, "/", StringComparison.Ordinal);
        }
转到:ParseUrlSegment()
private static IList<PathSubsegment> ParseUrlSegment(string segment, out Exception exception)
{
    int startIndex = 0;
    List<PathSubsegment> list = new List<PathSubsegment>();

        // 判断是不是区域
        int num2 = IndexOfFirstOpenParameter(segment, startIndex);
        if (num2 == -1)
        {
               //得到字符串
            string str = GetLiteral(segment.Substring(startIndex));
            if (str == null)
            {
                exception =  new ArgumentException("出错了");

                return null;
            }
            if (str.Length > 0)
            {
             list.Add(new LiteralSubsegment(str));
             exception = null;
             return list;
            }

        }
        //检查字符串,是否有“}”,没有就报错

        if (num2 >=1)
        {
            exception = new ArgumentException("出错了");

            return null;
        }

        int index = segment.IndexOf(‘}‘, num2 + 1);
        if (index != segment.Length - 1)
        {
            exception = new ArgumentException("出错了");
            return null;
        }
    //获取参数名字
        string parameterName = segment.Substring(num2 + 1, (index - num2) - 1);
        list.Add(new ParameterSubsegment(parameterName));

        exception = null;
        return list;
}

转到:IndexOfFirstOpenParameter
private static int IndexOfFirstOpenParameter(string segment, int startIndex)
{

        startIndex = segment.IndexOf(‘{‘, startIndex);
        return startIndex;

}

转到:GetLiteral



private static string GetLiteral(string segmentLiteral)
{
    string str = segmentLiteral.Replace("{{", "").Replace("}}", "");
    if (!str.Contains("{") && !str.Contains("}"))
    {
        return segmentLiteral.Replace("{{", "{").Replace("}}", "}");
    }
    return null;
}

List<PathSubsegment> list = new List<PathSubsegment>();

我们来看看PathSubsegment类吧,

  internal abstract class PathSubsegment
    {

        protected PathSubsegment() { }
    }

    //文字路劲部分,存放区域的,如:JB/{Home}/{Index}/{Id},他存放的就是JB
    internal sealed class LiteralSubsegment : PathSubsegment
    {
        // Methods
        public LiteralSubsegment(string literal)
        {
            this.Literal = literal;
        }
        // Properties
        public string Literal
        {
            get;
            set;
        }

    }
}

 //参数子段,存放参数的
    internal sealed class ParameterSubsegment : PathSubsegment
    {
        // Methods
        public ParameterSubsegment(string parameterName)
        {
            if (parameterName.StartsWith("*", StringComparison.Ordinal))
            {
                this.ParameterName = parameterName.Substring(1);
                this.IsCatchAll = true;
            }
            else
            {
                this.ParameterName = parameterName;
            }
        }
        // Properties
        public bool IsCatchAll {  get; private set; }
        public string ParameterName {get;  private set; }
    }

//存放 "/"符号的

internal sealed class SeparatorPathSegment : PathSegment
{
// Methods
public SeparatorPathSegment() { }
}


这两个都是他的子类
 

转到: ValidateUrlSegment

private static Exception ValidateUrlSegment(IList<PathSubsegment> pathSubsegments, HashSet<string> usedParameterNames, string pathSegment)
{

    Type type = null;
    foreach (PathSubsegment subsegment in pathSubsegments)
    {
        if ((type != null) && (type == subsegment.GetType()))
        {
            return new ArgumentException("出错了");
        }
        type = subsegment.GetType();
        if (!(subsegment is LiteralSubsegment))
        {
            ParameterSubsegment subsegment3 = subsegment as ParameterSubsegment;
            if (subsegment3 != null)
            {
                string parameterName = subsegment3.ParameterName;

                //判断里面有没有"/"
                if (!IsValidParameterName(parameterName))
                {
                    return new ArgumentException("出错了");
                }
                //判断有没有重复
                if (usedParameterNames.Contains(parameterName))
                {

                    return new ArgumentException("出错了");

                }
                usedParameterNames.Add(parameterName);
            }
        }
    }

    return null;
}

现在转到:   return new ParsedRoute(SplitUrlToPathSegments(pathSegments));

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Routing;

namespace MyRotue
{
    public class ParsedRoute
    {

        public ParsedRoute(IList<PathSegment> pathSegments)
        {
            this.PathSegments = pathSegments;
        }

        public RouteValueDictionary Match(string virtualPath, RouteValueDictionary defaultValues)
        {
            //拆分URL
            IList<string> source = RouteParser.SplitUrlToPathSegmentStrings(virtualPath);
            if (defaultValues == null)
            {
                defaultValues = new RouteValueDictionary();
            }

            //匹配值
            RouteValueDictionary matchedValues = new RouteValueDictionary();
            bool flag = false;
            bool flag2 = false;

            for (int i = 0; i < this.PathSegments.Count; i++)
            {
                PathSegment segment = this.PathSegments[i];
                if (source.Count <= i)
                {
                    flag = true;
                }
                string a = flag ? null : source[i];
                if (segment is SeparatorPathSegment)
                {
                    if (!flag && !string.Equals(a, "/", StringComparison.Ordinal))
                    {
                        return null;
                    }
                }
                else
                {
                    ContentPathSegment contentPathSegment = segment as ContentPathSegment;
                    if (contentPathSegment != null)
                    {
                        if (contentPathSegment.IsCatchAll)
                        {
                            this.MatchCatchAll(contentPathSegment, source.Skip<string>(i), defaultValues, matchedValues);
                            flag2 = true;
                        }
                        else if (!this.MatchContentPathSegment(contentPathSegment, a, defaultValues, matchedValues))
                        {
                            return null;
                        }
                    }
                }
            }
            if (!flag2 && (this.PathSegments.Count < source.Count))
            {
                for (int j = this.PathSegments.Count; j < source.Count; j++)
                {
                    if (!RouteParser.IsSeparator(source[j]))
                    {
                        return null;
                    }
                }
            }
            if (defaultValues != null)
            {
                foreach (KeyValuePair<string, object> pair in defaultValues)
                {
                    if (!matchedValues.ContainsKey(pair.Key))
                    {
                        matchedValues.Add(pair.Key, pair.Value);
                    }
                }
            }
            return matchedValues;
        }

        private void MatchCatchAll(ContentPathSegment contentPathSegment, IEnumerable<string> remainingRequestSegments, RouteValueDictionary defaultValues, RouteValueDictionary matchedValues)
        {
            object obj2;
            string str = string.Join(string.Empty, remainingRequestSegments.ToArray<string>());
            ParameterSubsegment subsegment = contentPathSegment.Subsegments[0] as ParameterSubsegment;
            if (str.Length > 0)
            {
                obj2 = str;
            }
            else
            {
                defaultValues.TryGetValue(subsegment.ParameterName, out obj2);
            }
            matchedValues.Add(subsegment.ParameterName, obj2);
        }

        private bool MatchContentPathSegment(ContentPathSegment routeSegment, string requestPathSegment, RouteValueDictionary defaultValues, RouteValueDictionary matchedValues)
        {
            if (string.IsNullOrEmpty(requestPathSegment))
            {
                if (routeSegment.Subsegments.Count <= 1)
                {
                    object obj2;
                    ParameterSubsegment subsegment = routeSegment.Subsegments[0] as ParameterSubsegment;
                    if (subsegment == null)
                    {
                        return false;
                    }
                    if (defaultValues.TryGetValue(subsegment.ParameterName, out obj2))
                    {
                        matchedValues.Add(subsegment.ParameterName, obj2);
                        return true;
                    }
                }
                return false;
            }

            int length = requestPathSegment.Length;
            int num2 = routeSegment.Subsegments.Count - 1;
            ParameterSubsegment subsegment2 = null;
            LiteralSubsegment subsegment3 = null;
            while (num2 >= 0)
            {
                int num3 = length;
                ParameterSubsegment subsegment4 = routeSegment.Subsegments[num2] as ParameterSubsegment;
                if (subsegment4 != null)
                {
                    subsegment2 = subsegment4;
                }
                else
                {
                    LiteralSubsegment subsegment5 = routeSegment.Subsegments[num2] as LiteralSubsegment;
                    if (subsegment5 != null)
                    {
                        subsegment3 = subsegment5;
                        int startIndex = length - 1;
                        if (subsegment2 != null)
                        {
                            startIndex--;
                        }
                        if (startIndex < 0)
                        {
                            return false;
                        }
                        int num5 = requestPathSegment.LastIndexOf(subsegment5.Literal, startIndex, StringComparison.OrdinalIgnoreCase);
                        if (num5 == -1)
                        {
                            return false;
                        }
                        if ((num2 == (routeSegment.Subsegments.Count - 1)) && ((num5 + subsegment5.Literal.Length) != requestPathSegment.Length))
                        {
                            return false;
                        }
                        num3 = num5;
                    }
                }
                if ((subsegment2 != null) && (((subsegment3 != null) && (subsegment4 == null)) || (num2 == 0)))
                {
                    int num6;
                    int num7;
                    if (subsegment3 == null)
                    {
                        if (num2 == 0)
                        {
                            num6 = 0;
                        }
                        else
                        {
                            num6 = num3;
                        }
                        num7 = length;
                    }
                    else if ((num2 == 0) && (subsegment4 != null))
                    {
                        num6 = 0;
                        num7 = length;
                    }
                    else
                    {
                        num6 = num3 + subsegment3.Literal.Length;
                        num7 = length - num6;
                    }
                    string str = requestPathSegment.Substring(num6, num7);
                    if (string.IsNullOrEmpty(str))
                    {
                        return false;
                    }
                    matchedValues.Add(subsegment2.ParameterName, str);
                    subsegment2 = null;
                    subsegment3 = null;
                }
                length = num3;
                num2--;
            }

            return true;
        }

        private IList<PathSegment> PathSegments { get; set; }

    }
}

好了,差不多了,你看到这里,然后再下载源码,调试一遍,就因该了路由匹配流程了。

 

源码中肯定,有很多错误,请指教。

源码:http://files.cnblogs.com/liek/MyRotue.rar

MVC 路由源码解析

时间: 2024-08-29 22:28:45

MVC 路由源码解析的相关文章

spring mvc源码解析

1.从DispatcherServlet开始     与很多使用广泛的MVC框架一样,SpringMVC使用的是FrontController模式,所有的设计都围绕DispatcherServlet 为中心来展开的.见下图,所有请求从DispatcherServlet进入,DispatcherServlet根据配置好的映射策略确定处理的 Controller,Controller处理完成返回ModelAndView,DispatcherServlet根据配置好的视图策略确定处理的 View,由V

spring MVC cors跨域实现源码解析 CorsConfiguration UrlBasedCorsConfigurationSource

spring MVC cors跨域实现源码解析 名词解释:跨域资源共享(Cross-Origin Resource Sharing) 简单说就是只要协议.IP.http方法任意一个不同就是跨域. spring MVC自4.2开始添加了跨域的支持. 跨域具体的定义请移步mozilla查看 使用案例 spring mvc中跨域使用有3种方式: 在web.xml中配置CorsFilter <filter> <filter-name>cors</filter-name> <

Laravel框架下路由的使用(源码解析)

本篇文章给大家带来的内容是关于Laravel框架下路由的使用(源码解析),有一定的参考价值,有需要的朋友可以参考一下,希望对你有所帮助. 前言 我的解析文章并非深层次多领域的解析攻略.但是参考着开发文档看此类文章会让你在日常开发中更上一层楼. 废话不多说,我们开始本章的讲解. 入口 Laravel启动后,会先加载服务提供者.中间件等组件,在查找路由之前因为我们使用的是门面,所以先要查到Route的实体类. 注册 第一步当然还是通过服务提供者,因为这是laravel启动的关键,在 RouteSer

“Tornado源码解析篇”导读索引

最近花了2周时间断断续续地阅读了 Tornado 的源码,写了“Tornado源码解析”这个系列专题.由于写得比较散,这里简单做一个索引与导读. 为什么要选择 Tornado 这个框架?先给大家讲一个小故事:赌王娱乐城 "[web.py inspired the] web framework we use at FriendFeed [and] the webapp framework that ships with App Engine..." — Brett Taylor, co-

SpringMVC源码解析- HandlerAdapter初始化

HandlerAdapter初始化时,主要是进行注解解析器初始化注册;返回值处理类初始化;全局注解@ControllerAdvice内容读取并缓存. 目录: 注解解析器初始化注册:@ModelAttribute(往model中添加属性) 注解解析器初始化注册:@InitBinder(用于注册校验器,参数编辑器等) 返回值处理returnValueHandlers初始化 全局的@ControllerAdvice注解使用类的@ModelAttribute 和 @InitBinder信息读取并缓存 注

Spring3.2 中 Bean 定义之基于 XML 配置方式的源码解析

Spring3.2 中 Bean 定义之基于 XML 配置方式的源码解析 本文简要介绍了基于 Spring 的 web project 的启动流程,详细分析了 Spring 框架将开发人员基于 XML 定义的 Bean 信息转换为 Spring 框架的 Bean Definition 对象的处理过程,向读者展示了 Spring 框架的奥妙之处,可以加深开发人员对 Spring 框架的理解. 0 评论: 秦 天杰, 软件工程师, IBM China 2013 年 9 月 02 日 内容 在 IBM

t-io 集群解决方案以及源码解析

t-io 集群解决方案以及源码解析 0x01 概要说明 本博客是基于老谭t-io showcase中的tio-websocket-showcase 示例来实现集群.看showcase 入门还是挺容易的,入坑(入门)请看老谭写的用t-io来写一个网页聊天室或客服是个怎样的体验. 要深入理解具体实现原理后续的业务扩展,把t-io玩6起来还需要耐心看看源码,看了之后我相信你一定会有收获的,祝你好运. 其实t-io 在2.4的版本中已加入的集群实现的逻辑代码,只是官方没有写文档以及完整的示例而已,在此不

DRF-解析器组件源码解析

解析器组件源码解析 解析器组件源码解析 1 执行request.data 开始找重装的request中的data方法 2 在dispatch找到重装的request def dispatch(self, request, *args, **kwargs): request = self.initialize_request(request, *args, **kwargs) ***这里开始找data 3 在initialize_request中找到实例request的类Request() def

.NET Core实战项目之CMS 第三章 入门篇-源码解析配置文件及依赖注入

作者:依乐祝 原文链接:https://www.cnblogs.com/yilezhu/p/9998021.html 写在前面 上篇文章我给大家讲解了ASP.NET Core的概念及为什么使用它,接着带着你一步一步的配置了.NET Core的开发环境并创建了一个ASP.NET Core的mvc项目,同时又通过一个实战教你如何在页面显示一个Content的列表.不知道你有没有跟着敲下代码,千万不要做眼高手低的人哦.这篇文章我们就会设计一些复杂的概念了,因为要对ASP.NET Core的启动及运行原