路由和数据传递
ASP.NET MVC 路由组成部分
理解路由概念
- 路由,源自网络中路由器概念。负责分配网络中数据传输的路径
- ASP.NET MVC 路由系统主要职责是:将各种URL请求转发给控制器处理。
例如:
这里给定了两个路径:
/book/detail/3.html
/account/login
第一个路径:/book/detail/3.html中控制器为book,动作方法为detail()
第二个路径:/account/login中控制器为account,动作方法为login()
由此可以了解到,路由系统是将请求通过分配不同控制器调用动作方法来执行命令。
掌握路由组成部分
routes.MapRoute() 路由完整定义
下面是一个完整的ADO.NET项目中的完整路由定义:
MapRoute(
string name,//名称
string url,//URL模式
object defaults,//默认值
object constraints,//约束
string[] namespaces//命名空间
)
name :路由器名称,区分不同路由配置
url:路由匹配方式
defaults:路由匹配方式中的默认值。
constaints:路由匹配方式中的约束,限制url的格式以及其他。
namespaces:去除路由匹配方式中的二义性,区分不同的路由来处理不同的请求。
后面这两个参数可以不指明。
URL模式匹配
路由定义
定义:
字面量{占位符}字面量{占位符}字面量…{占位符}字面量
字面量或占位符指的是任意字符或字符串
下面给出三种URL模式:
{table}/Details.aspx
Blog/{action}/{id}
{lan}-{country}/{action}
带大括号的内容在请求的时候可以随意输入,但是不带大括号的值是必须输入成指定的。在这里面,“/”也属于固定字符。
路由匹配规则
特殊规则
- 不能以"/“或”~“开头,整体不能包含”?"
例子:`
/{controller} //错误
~books/detail{id}//错误
{action}?id={id}//错误
- 占位符不能连续
例子:
{controller}{action}/{id}//错误
大括号和大括号之间必须有固定字符,哪怕是斜杠也是属于我们的固定字符。
路由匹配规则
- URL模式匹配原理
URL模式:
{controller}/{action}/{id}
实际URL
http://localhost/Home/Index/1
其中的:
{controller}与实际URL中的Home相互对应。
{action}与实际URL中的Index相对应。
{id}与实际URL中的1相对应。
URL模式:
blogs/{action}/{id}
实际URL
http://localhost/blogs/edit/2
其中的blogs是必须带的允许修改,在实际URL中也不允许修改
{action}与实际URL中的edit相对应
{id}与实际URL中的2相对应
路由匹配
- 按顺序将请求路径中的内容映射到占位符
- 字面量必须严格匹配
- URL模式不区分大小写
“*”匹配URL剩余部分
"{controller}/{action}/{query-name}/{*plus}"
以上面的URL定义,我们给出三种URL:
URL1:
/home/index/select/a/b
上面的URL中,home对应的是URL定义中的{controller}.,
index对应的是URL定义之后的{action},
最后a/b对应的就是带“*”的plus.
也就是匹配不上的部分,都归plus所有。
URL2:
/home/index/select/
上面的URL中,home对应的是URL定义中的{controller}.,
index对应的是URL定义之后的{action},
之后的puls的值就是null;
因为定义之中的plus有大括号代表着占位符,但是没有值,所以plus的值是null。
贪婪匹配规则
我们分析一下URL和URL模式还有路由数据:
URL为
/food.xml.aspx
URL模式为:
{filename}.{ext}
路由数据
filename = "food.xml"
ext = "aspx"
观察一下URL中有两个".“但是在URL模式中只有一个”.",在看读取出的路由数据中,是将URL分为两个部分,前面的"food.xml"是占位符filename的值,而后面的aspx为占位符ext 的值。
可以得出,在这种情况下,URL会自动识别最后的一个".“然后以最后一个”."为分界线,前的所有内容为前面占位符的,后面的内容为后面占位符的。
URL为
/xyzxyzxyzABCD
URL模式为:
{foo}xyz{bar}
路由数据则为:
foo="xyzxyz"
bar="ABCD"
在这种情况下:
三个xyz中,foo获取到的是前两个,所以foo的值是xyzxyz。
当然如果出现了这三个xyz相同字符重复,作为字面量xyz,系统在自动确认最后一个xyz为字面量,自动对应占位符foo的值为xyz。
后面一个,bar自动对应的是ABCD。
例子实现代码如下:
RouteConfig.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;
using System.Web.Routing;
namespace ch0301
{
public class RouteConfig
{
public static void RegisterRoutes(RouteCollection routes)
{
routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
routes.MapRoute(
name: "Default",
url: "{a}abc{c}",
defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional }//包含了控制器,动作方法
);
}
}
}
HomeController.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;
namespace ch0301.Controllers
{
public class HomeController : Controller
{
//
// GET: /Home/
public ActionResult Index()
{
return View();
}
}
}
Index.aspx
<%@ Page Language="C#" Inherits="System.Web.Mvc.ViewPage<dynamic>" %>
<!DOCTYPE html>
<html>
<head runat="server">
<meta name="viewport" content="width=device-width" />
<title>Index</title>
</head>
<body>
<div>
controller:<%=RouteData.Values["controller"] %><br/>
action:<%=RouteData.Values["action"] %><br />
id:<%=RouteData.Values["id"] %><br />
a:<%=RouteData.Values["a"] %><br/>
b:<%=RouteData.Values["b"] %><br />
c:<%=RouteData.Values["c"] %><br />
a:<%=RouteData.Values["a"] %><br />
c:<%=RouteData.Values["c"] %><br />
</div>
</body>
</html>
路由默认值
路由默认值
路由默认值:defaults属性中默认指明了信息。
defaults: new
{
controller = "Home",
action = "Index",
id = UrlParameter.Optional
}
可以访问的UR的形式为:
http://localhost/home/index/1
http://localhost/home/index
http://localhost/home
http://localhost/
含默认值路由匹配规则
如果没有指明默认值,必须通过URL提供对应占位符参数
routes.MapRoute(
name: "Default",
url: "{controller}/{action}/{id}",
defaults:new{action="index",id=0}
下面给出四中请求
1. /home //因为虽然没有设置默认值,但是我们已经给了action,id,这两个占位符赋了初始值,然后再提供控制器,所以就匹配成功
2. /home/index //同样如上,已经给定了id占位符的初始值,我们只用给定controller和action的值。
3. /home/index/2 //第三个将controller,action,id,都给定,相当于给了一个完整的url,所以能匹配成功
4. /
这四种请求能被匹配的是1,2,3。
第四个没有办法匹配:
因为第四个请求只给定了一个固定字符,在输入请求的时候没有提供一个正确的controller,action,id的值,但是初始值我们已经给定 action,id,但因为缺失controller的值,导致系统不知道要去哪里寻找页面,所以会报错。
如果指明中间参数默认值,该中间参数默认值不起作用。
routes.MapRoute(name:"Default",
url:"{controller}/{action}/{id}"
defaults:new{action="index"});
下面给定三种url,
1./home/index/2 //全部给定,完全可以匹配
2./book/detail/1//全部给定,完全可以匹配
3./home/0
第三个为什么不能匹配?
因为电脑只能从左到右去匹配,home匹配controller,但是这个给定的“0”会被电脑自动匹配成为action占位符的参数,这样id就没有值,id有无确定值其实不太重要在现在这个阶段,主要是找不到action的值,相当于没有使用我们之前给定的action。
如果路由含有字面量时,默认参数不起作用。
routes.MapRoute(
"Default",
"{controller}-{action}",
new { action="index"}
);
下面给他三种url请求:
1.home-index//url请求中包含了一套完整的控制器和动作方法。
2./home//不能匹配
3./home-//不能匹配
其中
第二个url请求因为只给定了controller控制器而且定义中存在字面量但是url请求中并没有该字面量存在,所以无法对应请求,所以无法匹配。
第三个url请去虽然存在这字面量字符,但是只提供了一个控制器。
其实如果想理解这句话的意思:路由含有字面量时,默认参数不起作用。
首先可以理解为默认参数提供的默认值可以看成默认的url,参数提供完全。
就如第三个例子说,计算机会去调用home,然后根据所给的action去寻找动作方法,但电脑会从左往右读取参数。在输入的时候要注意。根据如果路由含有字面量时,默认参数不起作用这一条,所以第三个不能成功匹配。
观察下列URL定义:
routes.MapRoute("Default",
"{controller}/{action}+{id}/{no}",
new { controller = "home",
action = "index",
id = "0",
no = "0" });
并给出以下URL请求:
1./home/index+1
2./home/index+10/N123
3./
4./home
5./home/index
6./home/index+
分析结果如下:
第一个给定了完整的控制器,动作方法和Id,所以能够匹配成功
第二个提供了完整的url,所以也匹配成功
第三个什么也没提供,但是我们默认值都给定了但是还是不能匹配成功,就是因为有字面量的存在,值得注意的是,上面例子中无效的是"{action}+{id}",其他的并不是被无效化。才会匹配不成功。
第四个与第三个同理,只给定了控制器。预设的参数又被无效化。
第五个因为给定了控制器和动作方法,但是没有把字面量给定,所以不能成功匹配。
第六个:
提供了控制器,动作方法,但是含有字面量的参数没有完全给定,所以匹配不成功。
总结
- 没有指明默认值时,URL必须包含对应的路由参数。
- 没有只指明路由中间参数时,该参数默认值不起作用
- 如果包含字面量时,默认参数不起作用。
路由约束和命名空间
路由约束
假设我们有一个Blog系统,定义URL模式,可根据URL中“年月日”读取文章。
我们定义一个URL模式为:
{year}/{month}/{day}
但是输入的时候,我们错误的输入的不合逻辑的URL请求。
/2012/11/test
这样日期是字符串类类型,我们该怎么做来避免这种情况出现呢。
我们可以用constraints参数来设定URL约束。
例子如下:
routes.MapRoute(
"bolg", "{year}/{month}/{day}",
new { controller = "blog", action = "index" },
constraints: new { year = @"\d{4}", month = @"\d{2}", day = @"\d{2}" }
);
这样就会避免出现以上情况。
命名空间
- 区分不同区域中的路由访问的控制器,以免请求冲突。
例子如下:
routes.MapRoute(
name: "Default",
url: "{year}/{mouth}/{day}",
defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional },
namespaces:new string[]{"Ch03.Controllers"}
数据传递
如何从控制器向试图传递数据?
使用session不是理想选择。根据数据的不同.NET MVC 提供了三种数据传递对象。——ViewData,ViewBag,TempData
使用ViewData对象传递数据
- 字典类型数据,通过键值对的形式来保存数据。
- 视图基类和控制器基类的属性
- viewData和viewBag数据相通。
- 传递一次就失效
- 适合传递单个数据,需要类型转换
使用ViewBag对象传递数据
- dynamic(动态类型)类型数据
- 视图基类和控制器基类的属性
- viewBag和viewData数据相通
- 传递一次就失效
- -适合传递单个数据,不需要类型转换。
使用TempData对象传递
- 字典类型数据,通过键值对的形式来保存数据。
- 视图基类和控制器属性基类的属性。
- TempData支持跨请求传递数据(跨控制器传递数据)
- 默认的数据保存机制是Session
- 在跨请求传递数据这个过程中,只要有一方拿到数据,TempData就会失效。
- 只能在一次请求过程中有效,在下一个请求就会失效。
- 主要用来跨多个动作方法传递数据
使用View()+Model形式传递数据
又称强类型视图传递
基本数据
- 第一步 ,动作方法中v使用View()对象来传递Model数据
Book book = manager.GetBookById(id);
return View(book);
- 第二步,视图声明为ViewPage类型
<%@ Page language="C#" MasterPageFile="~/Views/Shared/Site.Master"Inherits="System.Web.Mvc.ViewPage<BookShop.Models.Book>"%>
- 第三步,在视图中使用Model对象获取数据
作者:<%=Model.Author%>著<br/>
出版社:<%=Model.Publisher.Name %>
适合传递模型数据,不需要类型转换。