005. Asp.Net Routing与MVC 之三: 路由在MVC的使用
上次讲到请求如何激活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的使用的更多相关文章
- MVC 伪静态路由、MVC路由配置,实现伪静态。
前段时间,研究了一下mvc路由配置伪静态,在网上扒了很多最后还是行不通,所以我现在把这些心得整理出来,供大家分享: 1.mvc中默认路由配置是:http://localhost:24409/Home/ ...
- asp.net core 系列 6 MVC框架路由(下)
一.URL 生成 接着上篇讲MVC的路由,MVC 应用程序可以使用路由的 URL 生成功能,生成指向操作的 URL 链接. 生成 URL 可消除硬编码 URL,使代码更稳定.更易维护. 此部分重点介绍 ...
- 返璞归真 asp.net mvc (2) - 路由(System.Web.Routing)
原文:返璞归真 asp.net mvc (2) - 路由(System.Web.Routing) [索引页] [源码下载] 返璞归真 asp.net mvc (2) - 路由(System.Web.R ...
- asp.net MVC 5 路由 Routing
ASP.NET MVC ,一个适用于WEB应用程序的经典模型 model-view-controller 模式.相对于web forms一个单一的整块,asp.net mvc是由连接在一起的各种代码层 ...
- ASP.NET Core MVC 之路由(Routing)
ASP.NET Core MVC 路由是建立在ASP.NET Core 路由的,一项强大的URL映射组件,它可以构建具有理解和搜索网址的应用程序.这使得我们可以自定义应用程序的URL命名形式,使得它 ...
- 003. Asp.Net Routing与MVC 之一: 请求如何到达MVC
基础知识 本文用到的基础知识:URL.HttpModule 与 HttpHandler.IIS 的请求处理过程. URL HttpModule与HttpHandler IIS7.0的请求处理过程 OK ...
- ASP.NET路由[ASP.NET Routing]
ASP.NET路由[ASP.NET Routing] ASP.NET路由允许你在使用URL时不必匹配到网站中具体的文件,因为这个URL不必匹配到一个文件,你使用了描述用户行为且更容易被用户理解的URL ...
- .NET/ASP.NET Routing路由(深入解析路由系统架构原理)
阅读目录: 1.开篇介绍 2.ASP.NET Routing 路由对象模型的位置 3.ASP.NET Routing 路由对象模型的入口 4.ASP.NET Routing 路由对象模型的内部结构 4 ...
- .NET/ASP.NET Routing路由(深入解析路由系统架构原理)http://wangqingpei557.blog.51cto.com/1009349/1312422
阅读目录: 1.开篇介绍 2.ASP.NET Routing 路由对象模型的位置 3.ASP.NET Routing 路由对象模型的入口 4.ASP.NET Routing 路由对象模型的内部结构 4 ...
随机推荐
- Willem, Chtholly and Seniorious
Willem, Chtholly and Seniorious https://codeforces.com/contest/897/problem/E time limit per test 2 s ...
- JavaSE基础知识(1)—初识Java
一.JAVA的背景 JAVA本身隶属的公司的是sun公司(创始公司) JAVA创始人:詹姆斯 高斯林 09年被oracle收购 JAVA的前身是Oak 二.JAVA的版本 95年 JAVA诞生96年 ...
- Pandas 合并 concat
pandas处理多组数据的时候往往会要用到数据的合并处理,使用 concat是一种基本的合并方式.而且concat中有很多参数可以调整,合并成你想要的数据形式. 1.axis(合并方向):axis=0 ...
- window 安装mysql
常见错误:ERROR 1045 (28000): Access denied for user 'root'@'localhost' (using password: YES) 密码输入错误:无法远程 ...
- 远程连接centos6.5
安装上传下载工具: 直接yum -y install lrzsz 下载数据到本地下载目录:sz filename1 filename2 … 上传数据到远程:执行rz –be 命令,客户端会弹出上传窗口 ...
- Mysql知识点个人整理
1.概念 数据库:保存有组织的数据的容器. 表: 某种特定类型数据的结构化清单 模式:关于数据库和表的布局和特性的信息?(有时指数据库) 主键: primary key 一个列或一组列,其值能唯一区分 ...
- JS判断是否有js、css文件的引入方法
在页面头部有个 <script type="text/javascript" src="abc.js"></script> <li ...
- js-function复制变量值和传递参数
<title>function复制变量值</title></head><body> <script> var a={ num:10 } // ...
- Coding能力提升小技巧
一.使用变量的一般原则 1.变量初始化原则: 通常在变量声明时初始化; 在靠近变量第一次使用的位置初始化; 在类的构造函数里初始化变量. 2.作用域: 使变量引用局部化,即把引用到变量的地方尽可能集中 ...
- .NET Core微服务之路:利用DotNetty实现一个简单的通信过程
上一篇我们已经全面的介绍过<基于gRPC服务发现与服务治理的方案>,我们先复习一下RPC的调用过程(笔者会在这一节的几篇文章中反复的强调这个过程调用方案),看下图