005. Asp.Net Routing与MVC 之三: 路由在MVC的使用

时间:2022-09-05 14:42:12

上次讲到请求如何激活Controller和Action,这次讲下MVC中路由的使用。
本次两个关注点:

  • 遗留:ModelBinder.BindModel的过程
  • MVC中路由的使用
  • MVC 5中的Action新特性

一、ModelBinder.BindModel的过程

MVCHander –>  ProcessRequest()
xxxxxController\ControllerFactory
IController.Excute();
ControllerBase.Excute().ExcuteCore()
Controller.ExecuteCore()  { . GetActionName  ;  IActionInvoker.InvokeAction() }

IActionInvoker.InvokeAction() {

get methodInfo  //sys

处理参数  // BindModel

methodInfo.invoke();//sys

}

在MVC源码中,ControllerActionInvoker.InvokeAction()
我们看看BindModel做了些什么。

   1:  

   2: public class DefaultModelBinder : IModelBinder

   3: {

   4:     /// <summary>

   5:     /// ControllerContext (控制器上下文,包含Controller 类型实例,和 当前请求上下文RequestContext)

   6:     /// MVC Model Bind, 从  ControllerContext 中,为当前参数(确定的类型 和 名称)赋值。

   7:     /// </summary>

   8:     /// <param name="controllerContext"></param>

   9:     /// <param name="modelName"></param>

  10:     /// <param name="modelType"></param>

  11:     /// <returns></returns>

  12:     public object BindModel(ControllerContext controllerContext, string modelName, Type modelType)

  13:     {

  14:         // 值类型 或 对象值类型(string),即简单点说:值类型

  15:         if (modelType.IsValueType || typeof(string) == modelType)

  16:         {

  17:             object instance;

  18:             //获取值类型的 值实例,并返回

  19:             if (GetValueTypeInstance(controllerContext, modelName, modelType, out instance))

  20:             {

  21:                 return instance;

  22:             };

  23:             return Activator.CreateInstance(modelType);

  24:         }

  25:  

  26:         //不是值类型,即 对象类型

  27:         //使用类型默认构造函数,创建一个实例

  28:         object modelInstance = Activator.CreateInstance(modelType);

  29:         //下边就是一个循环的过程,查找对象的属性,值类型的就赋值,循环属性直至完成

  30:         foreach (PropertyInfo property in modelType.GetProperties())

  31:         {

  32:             //忽略了 action参数 的对象类型中“对象类型”的属性

  33:             if (!property.CanWrite || (!property.PropertyType.IsValueType && property.PropertyType != typeof(string)))

  34:             {

  35:                 continue;

  36:             }

  37:             object propertyValue;

  38:             if (GetValueTypeInstance(controllerContext, property.Name, property.PropertyType, out propertyValue))

  39:             {

  40:                 property.SetValue(modelInstance, propertyValue, null);

  41:             }

  42:         }

  43:         return modelInstance;

  44:     }

  45:  

  46:     /// <summary>

  47:     /// 为值类型 或 对象值类型(string),(即简单点说:值类型)获取值。

  48:     ///  结构体(数值,bool,自定义结构体struct),枚举enum,可空类型 

  49:     /// </summary>

  50:     /// <param name="controllerContext"></param>

  51:     /// <param name="modelName"></param>

  52:     /// <param name="modelType"></param>

  53:     /// <param name="value"></param>

  54:     /// <returns></returns>

  55:     private bool GetValueTypeInstance(ControllerContext controllerContext, string modelName, Type modelType, out object value)

  56:     {

  57:         //HttpPost 提交,使用窗体变量集合  

  58:         var form = HttpContext.Current.Request.Form;

  59:         string key;

  60:         //form集合不为空

  61:         if (null != form)

  62:         {

  63:             //窗体变量集合 AllKeys 里查找 “modelName” 变量。

  64:             key = form.AllKeys.FirstOrDefault(k => string.Compare(k, modelName, true) == 0);

  65:             if (key != null)

  66:             {

  67:                 //?结构体

  68:                 //value =  Convert.ChangeType(form[key]., modelType);

  69:                 var curKeyVal = form[key];

  70:                 value = Convert.ChangeType(curKeyVal, modelType);

  71:                 return true;

  72:             }

  73:         }

  74:         //HttpGet 提交,使用路由值中的 QueryString

  75:         key = controllerContext.RequestContext.RouteData.Values

  76:             //更快!

  77:             //.Where(item => string.Equals(item.Key, modelName,StringComparison.InvariantCultureIgnoreCase))

  78:             .Where(item => string.Compare(item.Key, modelName, true) == 0)

  79:             //get提交中,只取当前key的第一个匹配项的值

  80:             .Select(item => item.Key).FirstOrDefault();

  81:         if (null != key)

  82:         {

  83:             value = Convert.ChangeType(controllerContext.RequestContext.RouteData.Values[key], modelType);

  84:             return true;

  85:         }

  86:  

  87:         //http://msdn.microsoft.com/zh-cn/library/system.web.routing.routedata.datatokens.aspx

  88:         //RouteData.DataTokens 属性

  89:         //与RouteData.Values类似的键值对方式,允许按key获取值。

  90:         //没试出与 RouteData.Values的区别???

  91:         key = controllerContext.RequestContext.RouteData.DataTokens

  92:             .Where(item => string.Compare(item.Key, modelName, true) == 0)

  93:             .Select(item => item.Key).FirstOrDefault();

  94:         if (null != key)

  95:         {

  96:             value = Convert.ChangeType(controllerContext.RequestContext.RouteData.DataTokens[key], modelType);

  97:             return true;

  98:         }

  99:         value = null;

 100:         return false;

 101:     }

 102: }

概括的说,就是

1、ControllerActionInvoker.InvokeAction() 里,获取当前 MethodInfo 的参数数组(在完整的MVC项目中,在InvokeAction时,还有各种过滤器的操作,ActionExecutedContext Action执行上下文的构建等)

2、循环 MethodInfo 的 参数数组,调用 IModelBinder.BindModel()为参数赋值

3、在IModelBinder.BindModel(),判断参数的类型,是值类型的,直接从 RouteData里获取路由值,转换成对应的类型输出并返回。

4、是引用类型的,调用该类型的默认构造函数,实例化一个该类型,并循环其公开属性,重复值类型的赋值方式,赋值输出并返回。

二、MVC中路由的使用

MVC中,用户访问的地址并不映射到服务器中对应的文件,而是映射到对应Control里对应的ActionMethod,由ActionMethod来决定返回用户什么样的信息。而把用户访问的地址对应到对应的Action(当然也可以是对应的文件)的工作有路由系统完成,这其中许多复杂的处理由.net自动完成,而开发者需要告诉.net用户的访问地址和对应Action的具体映射关系。
MVC中路由系统可以完成两件任务:
(1). 处理从用户接收到得URL映射到对应的Action;
(2). 将某个Action根据路由系统的映射关系,动态生成URL,(当网站程序结构改变时,该URL同样会自动改变);

1、注册:

在MVC项目的 RouteConfig.RegisterRoutes()里注册,一个典型的 RouteConfig内容如下:

   1: public class RouteConfig

   2: {

   3:     public static void RegisterRoutes(RouteCollection routes)

   4:     {

   5:         routes.IgnoreRoute("{resource}.axd/{*pathInfo}");

   6:  

   7:         Route myRoute1 = new Route("mytest/{controller}/{action}/{id}", new MvcRouteHandler());        

   8:         routes.Add("MyRoute1", myRoute1);

   9:  

  10:         //玄机就在这了,这个MapRoute位于System.Web.Mvc.RouteCollectionExtensions

  11:         //看RouteCollectionExtensions里面做了什么

  12:         routes.MapRoute(

  13:             name: "Default",//命名

  14:             url: "{controller11}/{action2123}/{id}",  //url 这里使用了路由规则表达式,在《Asp.Net Routing与MVC 之三: 路由在MVC的使用》里说

  15:             //id = UrlParameter.Optional  可选的路由参数

  16:             defaults: new { controller11 = "Home", action2123 = "Index", id = UrlParameter.Optional }

  17:         );

  18:     //不存在的url

  19:     routes.MapRoute(

  20:             name: "DefaultNonExist",//命名

  21:             url: "{controller11}/{action2123}/{id}",  //url 这里使用了路由规则表达式,在《Asp.Net Routing与MVC 之三: 路由在MVC的使用》里说

  22:             //id = UrlParameter.Optional  可选的路由参数

  23:             defaults: new { controller11 = "Home", action2123 = "IndexNonExist", id = UrlParameter.Optional }

  24:         );

  25:  

  26:  

  27:  

  28:         //要启用MVC5的新特性,这里需要启用“映射特性路由”

  29:         routes.MapMvcAttributeRoutes();

  30:     }

  31: }

2、处理接受到的URL

第二个参数中, "ABC{category}/{controller}/Page{page}/DEF",路由系统会根据此处的参数,把用户请求的URL和这里的参数进行匹配,有两种映射匹配方式:

(1).动态内容,放在{}里的,即为要匹配的参数名,比如controller,action中的内容均会被匹配并赋值给controller,action参数。然后依据controller,action参数的值,查找controller类型,并到此controller里需找对应的ActionMethod。

(2).静态内容,放在{}以外的内容,会将此处每一个字符同URL进行比较,比如mytest/…这些

如果URL和该参数的两种比较方式比均均匹配成功,则为完全匹配。接下来相应的Controller和Action进行执行处理。
否则,未匹配成 功的URL项:比如action2123,会按默认的参数去匹配Action,如果默认参数匹配不成功,则抛出异常

如果相同的URL,注册了多个重复的MapRoute时,仅有第一个会生效、。

3、将对应的Action转换为URL
   路由系统的第二个功能就是实现把Action转换为对应的URL。

1,生成链接:<a>
   @Html.ActionLink("About this application", "Index", "Home",new {id = "myAnchorID", @class = "myCSSClass"})第四个参数可以为生成的链接提供属性; 当提供的参数和路由系统中的参数不一致时,会生成QueryString:?..=..
   2,生成URL字符串:仅仅产生URL的字符串,即href后的内容
    @Html.Action(),用法同ActionLink一致。

三、MVC5中的新的路由特性

1、什么是Attribute路由?怎么样启用Attribute路由?

  微软在 ASP.NET MVC5 中引入了一种新型路由:Attribute路由,顾名思义,Attribute路由是通过Attribute来定义路由。当然,MVC5也支持以前定义路由的方式,你可以在一个项目中混合使用这两种方式来定义路由。  

在MVC5中,我们可以把路由定义和 Action 放在一起,新的方式:

   1: //MVC 5 的新路由特性,我们不用去 RouteConfig中劳神了

   2: [Route("{testId:int}/{testName}")]

   3: public ActionResult About(int testId,string testName)

   4: {

   5:     ViewBag.Message =string.Format( "Your application description page.test info :{0},{1}",testId,testName);

   6:  

   7:     return View();

   8: }

2、URL可选参数和默认值

  我们可以使用问号“?”来标记一个可选参数,也可以对参数设定默认值:

   1: //可选的特性路由参数 及默认值

   2: [Route("{testId?}/{testName=JasonLiu}")]

   3: public ActionResult About1(int? testId,string testName)

   4: {

   5:     ViewBag.Message = string.Format("Your application description page.test info :{0},{1}", testId,testName);

   6:  

   7:     return View();

   8: }

3、路由前缀与默认路由

  有时候在同一个 Controller 中,所有 Action 匹配的 URL 都拥有相同的前缀,当某个Action 不想使用该前缀时,我们用 ~来忽略她。、

并且可以指定当前路由的默认值。

   1: [RoutePrefix("myhome")] //此Controller的默认路由前缀

   2: [Route("{action=index}")] //此Controller对应路由的默认action值

   3: [Route("{testName=JasonLiu}")] //如上类推

   4: public class HomeController : Controller

   5: { 

   6:  

   7:     public ActionResult Index()

   8:     { 

   9:         return View();

  10:     }

  11:  

  12:     //MVC 5 的新路由特性,我们不用去 RouteConfig中劳神了

  13:     [Route("{testId:int}/{testName}")]

  14:     public ActionResult About(int testId,string testName)

  15:     {

  16:         ViewBag.Message =string.Format( "Your application description page.test info :{0},{1}",testId,testName);

  17:  

  18:         return View();

  19:     }

  20:  

  21:     //可选的特性路由参数 及默认值

  22:     [Route("{testId?}/{testName=JasonLiu}")]

  23:     public ActionResult About1(int? testId, string testName)

  24:     {

  25:         ViewBag.Message = string.Format("Your application description page.test info :{0},{1}", testId, testName);

  26:  

  27:         return View();

  28:     }

  29:  

  30:     [Route("~/contact")]

  31:     public ActionResult Contact()

  32:     {

  33:         ViewBag.Message = "Your contact page.";

  34:  

  35:         return View();

  36:     }

  37: }

别的,路由约束之类,与之前普通路由定义遵守相同规则。

005. Asp.Net Routing与MVC 之三: 路由在MVC的使用的更多相关文章

  1. MVC 伪静态路由、MVC路由配置,实现伪静态。

    前段时间,研究了一下mvc路由配置伪静态,在网上扒了很多最后还是行不通,所以我现在把这些心得整理出来,供大家分享: 1.mvc中默认路由配置是:http://localhost:24409/Home/ ...

  2. asp&period;net core 系列 6 MVC框架路由&lpar;下&rpar;

    一.URL 生成 接着上篇讲MVC的路由,MVC 应用程序可以使用路由的 URL 生成功能,生成指向操作的 URL 链接. 生成 URL 可消除硬编码 URL,使代码更稳定.更易维护. 此部分重点介绍 ...

  3. 返璞归真 asp&period;net mvc &lpar;2&rpar; - 路由&lpar;System&period;Web&period;Routing&rpar;

    原文:返璞归真 asp.net mvc (2) - 路由(System.Web.Routing) [索引页] [源码下载] 返璞归真 asp.net mvc (2) - 路由(System.Web.R ...

  4. asp&period;net MVC 5 路由 Routing

    ASP.NET MVC ,一个适用于WEB应用程序的经典模型 model-view-controller 模式.相对于web forms一个单一的整块,asp.net mvc是由连接在一起的各种代码层 ...

  5. ASP&period;NET Core MVC 之路由&lpar;Routing&rpar;

     ASP.NET Core MVC 路由是建立在ASP.NET Core 路由的,一项强大的URL映射组件,它可以构建具有理解和搜索网址的应用程序.这使得我们可以自定义应用程序的URL命名形式,使得它 ...

  6. 003&period; Asp&period;Net Routing与MVC 之一: 请求如何到达MVC

    基础知识 本文用到的基础知识:URL.HttpModule 与 HttpHandler.IIS 的请求处理过程. URL HttpModule与HttpHandler IIS7.0的请求处理过程 OK ...

  7. ASP&period;NET路由&lbrack;ASP&period;NET Routing&rsqb;

    ASP.NET路由[ASP.NET Routing] ASP.NET路由允许你在使用URL时不必匹配到网站中具体的文件,因为这个URL不必匹配到一个文件,你使用了描述用户行为且更容易被用户理解的URL ...

  8. &period;NET&sol;ASP&period;NET Routing路由(深入解析路由系统架构原理)

    阅读目录: 1.开篇介绍 2.ASP.NET Routing 路由对象模型的位置 3.ASP.NET Routing 路由对象模型的入口 4.ASP.NET Routing 路由对象模型的内部结构 4 ...

  9. &period;NET&sol;ASP&period;NET Routing路由(深入解析路由系统架构原理)http&colon;&sol;&sol;wangqingpei557&period;blog&period;51cto&period;com&sol;1009349&sol;1312422

    阅读目录: 1.开篇介绍 2.ASP.NET Routing 路由对象模型的位置 3.ASP.NET Routing 路由对象模型的入口 4.ASP.NET Routing 路由对象模型的内部结构 4 ...

随机推荐

  1. &period;NET生成带Logo的二维码

    使用ThoughtWorks.QRCode生成,利用这个库来生成带Logo的二维码(就是中间嵌了一个图片的二维码),直接见代码: HttpContext context = HttpContext.C ...

  2. &lbrack;Leetcode&rsqb; Wildcard Matching

    Implement wildcard pattern matching with support for '?' and '*'. '?' Matches any single character. ...

  3. &lbrack;Socket&rsqb;BSD Socket网络通信

    http://blog.csdn.net/dongfengsun/article/details/4802925 文章有一些错误 #define KENTER @"/r/n" 应该 ...

  4. PyCharm设置字体

    pycharm 是很好的一个IDE,就是默认字体太小了,真的太小了,改字体的地方很隐晦.找了半天,贴图: 然后发现size 不能更改,所以,点击save as创建一个方案,然后修改自己的方案.这个时候 ...

  5. 性能测试分享:Jmeter多机协作原理

    性能测试分享:Jmeter多机协作原理   poptest是国内唯一一家培养测试开发工程师的培训机构,以学员能胜任自动化测试,性能测试,测试工具开发等工作为目标.如果对课程感兴趣,请大家咨询qq:90 ...

  6. 阿里云服务器 通过JavaMail发送邮箱STMP问题( 25端口被禁用 使用SSL协议465端口 )

    我们传统使用的比较简单的是 STMP 25端口收发邮件 今天发现刚购买的阿里云服务器不能作为客户端通过STMP 25端口发送邮件 开始在网上有说发现是JDK1.8的原因,然后自己也把JDK1.8换到了 ...

  7. H5开发中的故障

    本篇博文会不断的收录我在做H5页面时遇到的问题以及解决方案,当然有的问题,我也没有遇到好的解决方案,所以如果你有解决的办法,请务必不吝赐教! H5开发中的故障       微信APP返回按钮不刷新页面 ...

  8. &period;net程序员面试小结(内附一些面试题和答案)

    今天下午去面试,面试官和HR小姐姐都很好,没有做面试题,用聊天的方式来交流技术,整个过程很轻松,从中也学到了很多知识. 下面就来总结一下面试过程. 一.深刻了解自己的简历 无论是HR还是技术面试人,首 ...

  9. centos7系统安装完成后一些基本的优化

    安装完centos7.3后,做一些基本的操作 基本操作一:主机名 centos7有一个新的修改主机名的命令hostnamectl # hostnamectl set-hostname --static ...

  10. 关于servlet3&period;0中的异步servlet

    刚看了一下*上的介绍,servlet3.0是2009年随着JavaEE6.0发布的: 到现在已经有六七年的时间了,在我第一次接触java的时候(2011年),servlet3.0就已经出现很久了 ...