先说一下基本的路由规则原则。基本的路由规则是从特殊到一般排列,也就是最特殊(非主流)的规则在最前面,最一般(万金油)的规则排在最后。这是因为匹配路由规则也是照着这个顺序的。如果写反了,那么即便你路由规则写对了那照样坐等404.
xd 首先说url的构造。 其实这个也谈不上构造,只是语法特性吧。
url构造
命名参数规范+匿名对象routes.maproute(name: "default",url: "{controller}/{action}/{id}", defaults: new { controller = "home", action = "index", id = urlparameter.optional } );
构造路由然后添加route myroute = new route("{controller}/{action}", new mvcroutehandler());
routes.add("myroute", myroute);
直接方法重载+匿名对象routes.maproute("shopschema", "shop/{action}", new { controller = "home" });
个人觉得第一种比较易懂,第二种方便调试,第三种写起来比较效率吧。各取所需吧。本文行文偏向于第三种。
路由规则
1.默认路由(mvc自带)routes.maproute(
"default", // 路由名称
"{controller}/{action}/{id}", // 带有参数的 url
new { controller = "home", action = "index", id = urlparameter.optional } // 参数默认值 (urlparameter.optional-可选的意思) );
2.静态url段routes.maproute("shopschema2", "shop/oldaction", new { controller = "home", action = "index" });
routes.maproute("shopschema", "shop/{action}", new { controller = "home" });
routes.maproute("shopschema2", "shop/oldaction.js",
new { controller = "home", action = "index" });
没有占位符路由就是现成的写死的。
比如这样写然后去访问http://localhost:xxx/shop/oldaction.js,response也是完全没问题的。 controller , action , area这三个保留字就别设静态变量里面了。
3.自定义常规变量url段(好吧这翻译暴露智商了)routes.maproute("myroute2", "{controller}/{action}/{id}", new { controller = "home", action = "index", id = "defaultid" });
这种情况如果访问 /home/index 的话,因为第三段(id)没有值,根据路由规则这个参数会被设为defaultid
这个用viewbag给title赋值就能很明显看出
viewbag.title = routedata.values["id"];
图不贴了,结果是标题显示为defaultid。 注意要在控制器里面赋值,在视图赋值没法编译的。
4.再述默认路由
然后再回到默认路由。 urlparameter.optional这个叫可选url段.路由里没有这个参数的话id为null。 照原文大致说法,这个可选url段能用来实现一个关注点的分离。刚才在路由里直接设定参数默认值其实不是很好。照我的理解,实际参数是用户发来的,我们做的只是定义形式参数名。但是,如果硬要给参数赋默认值的话,建议用语法糖写到action参数里面。比如:
public actionresult index(string id = "abcd"){viewbag.title = routedata.values["id"];return view();}
5.可变长度路由。routes.maproute("myroute", "{controller}/{action}/{id}/{*catchall}", new { controller = "home", action = "index", id = urlparameter.optional });
在这里id和最后一段都是可变的,所以 /home/index/dabdafdaf 等效于 /home/index//abcdefdjldfiaeahfoeiho 等效于 /home/index/all/delete/perm/.....
6.跨命名空间路由
这个提醒一下记得引用命名空间,开启iis网站不然就是404。这个非常非主流,不建议瞎搞。
routes.maproute("myroute","{controller}/{action}/{id}/{*catchall}", new { controller = "home", action = "index", id = urlparameter.optional },new[] { "urlsandroutes.additionalcontrollers", "urlsandroutes.controllers" });
但是这样写的话数组排名不分先后的,如果有多个匹配的路由会报错。 然后作者提出了一种改进写法。
routes.maproute("addcontollerroute","home/{action}/{id}/{*catchall}",new { controller = "home", action = "index", id = urlparameter.optional },new[] { "urlsandroutes.additionalcontrollers" });
routes.maproute("myroute", "{controller}/{action}/{id}/{*catchall}", new { controller = "home", action = "index", id = urlparameter.optional },new[] { "urlsandroutes.controllers" });
这样第一个url段不是home的都交给第二个处理 最后还可以设定这个路由找不到的话就不给后面的路由留后路啦,也就不再往下找啦。
route myroute = routes.maproute("addcontollerroute",
"home/{action}/{id}/{*catchall}",
new { controller = "home", action = "index", id = urlparameter.optional },
new[] { "urlsandroutes.additionalcontrollers" }); myroute.datatokens["usenamespacefallback"] = false;
7.正则表达式匹配路由routes.maproute("myroute", "{controller}/{action}/{id}/{*catchall}",
new { controller = "home", action = "index", id = urlparameter.optional },
new { controller = "^h.*"},
new[] { "urlsandroutes.controllers"});
约束多个urlroutes.maproute("myroute", "{controller}/{action}/{id}/{*catchall}",
new { controller = "home", action = "index", id = urlparameter.optional },
new { controller = "^h.*", action = "^index$|^about$"},
new[] { "urlsandroutes.controllers"});
8.指定请求方法routes.maproute("myroute", "{controller}/{action}/{id}/{*catchall}",
new { controller = "home", action = "index", id = urlparameter.optional },
new { controller = "^h.*", action = "index|about", httpmethod = new httpmethodconstraint("get") },
new[] { "urlsandroutes.controllers" });
9. webform支持routes.mappageroute("", "", "~/default.aspx");
routes.mappageroute("list", "items/{action}", "~/items/list.aspx", false, new routevaluedictionary { { "action", "all" } });
routes.mappageroute("show", "show/{action}", "~/show.aspx", false, new routevaluedictionary { { "action", "all" } });
routes.mappageroute("edit", "edit/{id}", "~/edit.aspx", false, new routevaluedictionary { { "id", "1" } }, new routevaluedictionary { { "id", @"\d+" } });
具体的可以看
使用asp.net4新特性路由创建webform应用
或者官方msdn
10.mvc5的routeattribute
首先要在路由注册方法那里
//启用路由特性映射
routes.mapmvcattributeroutes();
这样
[route("login")]
route特性才有效.该特性有好几个重载.还有路由约束啊,顺序啊,路由名之类的.
其他的还有路由前缀,路由默认值[routeprefix("reviews")]<br>[route("{action=index}")]<br>public class reviewscontroller : controller<br>{<br>}
路由构造// eg: /users/5
[route("users/{id:int}"]
public actionresult getuserbyid(int id) { ... }
// eg: users/ken
[route("users/{name}"]
public actionresult getuserbyname(string name) { ... }
参数限制// eg: /users/5
// but not /users/10000000000 because it is larger than int.maxvalue,
// and not /users/0 because of the min(1) constraint.
[route("users/{id:int:min(1)}")]
public actionresult getuserbyid(int id) { ... }
constraint
description
example
alpha
matches uppercase or lowercase latin alphabet characters (a-z, a-z)
{x:alpha}
bool
matches a boolean value.
{x:bool}
datetime
matches a datetime value.
{x:datetime}
decimal
matches a decimal value.
{x:decimal}
double
matches a 64-bit floating-point value.
{x:double}
float
matches a 32-bit floating-point value.
{x:float}
guid
matches a guid value.
{x:guid}
int
matches a 32-bit integer value.
{x:int}
length
matches a string with the specified length or within a specified range of lengths.
{x:length(6)} {x:length(1,20)}
long
matches a 64-bit integer value.
{x:long}
max
matches an integer with a maximum value.
{x:max(10)}
maxlength
matches a string with a maximum length.
{x:maxlength(10)}
min
matches an integer with a minimum value.
{x:min(10)}
minlength
matches a string with a minimum length.
{x:minlength(10)}
range
matches an integer within a range of values.
{x:range(10,50)}
regex
matches a regular expression.
{x:regex(^\d{3}-\d{3}-\d{4}$)}
具体的可以参考
attribute routing in asp.net mvc 5
对我来说,这样的好处是分散了路由规则的定义.有人喜欢集中,我个人比较喜欢这种灵活的处理.因为这个action定义好后,我不需要跑到配置那里定义对应的路由规则
11.最后还是不爽的话自己写个类实现 irouteconstraint的匹配方法。using system;
using system.collections.generic;
using system.linq;
using system.web;
using system.web.routing;
/// <summary>
/// if the standard constraints are not sufficient for your needs, you can define your own custom constraints by implementing the irouteconstraint interface.
/// </summary>
public class useragentconstraint : irouteconstraint
{
private string requireduseragent;
public useragentconstraint(string agentparam)
{
requireduseragent = agentparam;
}
public bool match(httpcontextbase httpcontext, route route, string parametername,
routevaluedictionary values, routedirection routedirection)
{
return httpcontext.request.useragent != null &&
httpcontext.request.useragent.contains(requireduseragent);
}
}
routes.maproute("chromeroute", "{*catchall}",
new { controller = "home", action = "index" },
new { customconstraint = new useragentconstraint("chrome") },
new[] { "urlsandroutes.additionalcontrollers" });
比如这个就用来匹配是否是用谷歌浏览器访问网页的。
12.访问本地文档routes.routeexistingfiles = true;
routes.maproute("diskfile", "content/staticcontent.html", new { controller = "customer", action = "list", });
浏览网站,以开启 iis express,然后点显示所有应用程序-点击网站名称-配置(applicationhost.config)-搜索urlroutingmodule节点
<add name="urlroutingmodule-4.0" type="system.web.routing.urlroutingmodule" precondition="managedhandler,runtimeversionv4.0" />
把这个节点里的precondition删除,变成
<add name="urlroutingmodule-4.0" type="system.web.routing.urlroutingmodule" precondition="" />
13.直接访问本地资源,绕过了路由系统routes.ignoreroute("content/{filename}.html");
文件名还可以用 {filename}占位符。
ignoreroute方法是routecollection里面stoproutinghandler类的一个实例。路由系统通过硬-编码识别这个handler。如果这个规则匹配的话,后面的规则都无效了。 这也就是默认的路由里面routes.ignoreroute("{resource}.axd/{*pathinfo}");写最前面的原因。
路由测试(在测试项目的基础上,要装moq)pm> install-package moq
using system;
using microsoft.visualstudio.testtools.unittesting;
using system.web;
using moq;
using system.web.routing;
using system.reflection;
[testclass]
public class routestest
{
private httpcontextbase createhttpcontext(string targeturl = null, string httpmethod = "get")
{
// create the mock request
mock<httprequestbase> mockrequest = new mock<httprequestbase>();
mockrequest.setup(m => m.apprelativecurrentexecutionfilepath)
.returns(targeturl);
mockrequest.setup(m => m.httpmethod).returns(httpmethod);
// create the mock response
mock<httpresponsebase> mockresponse = new mock<httpresponsebase>();
mockresponse.setup(m => m.applyapppathmodifier(
it.isany<string>())).returns<string>(s => s);
// create the mock context, using the request and response
mock<httpcontextbase> mockcontext = new mock<httpcontextbase>();
mockcontext.setup(m => m.request).returns(mockrequest.object);
mockcontext.setup(m => m.response).returns(mockresponse.object);
// return the mocked context
return mockcontext.object;
}
private void testroutematch(string url, string controller, string action, object routeproperties = null, string httpmethod = "get")
{
// arrange
routecollection routes = new routecollection();
routeconfig.registerroutes(routes);
// act - process the route
routedata result = routes.getroutedata(createhttpcontext(url, httpmethod));
// assert
assert.isnotnull(result);
assert.istrue(testincomingrouteresult(result, controller, action, routeproperties));
}
private bool testincomingrouteresult(routedata routeresult, string controller, string action, object propertyset = null)
{
func<object, object, bool> valcompare = (v1, v2) =>
{
return stringcomparer.invariantcultureignorecase
.compare(v1, v2) == 0;
};
bool result = valcompare(routeresult.values["controller"], controller)
&& valcompare(routeresult.values["action"], action);
if (propertyset != null)
{
propertyinfo[] propinfo = propertyset.gettype().getproperties();
foreach (propertyinfo pi in propinfo)
{
if (!(routeresult.values.containskey(pi.name)
&& valcompare(routeresult.values[pi.name],
pi.getvalue(propertyset, null))))
{
result = false;
break;
}
}
}
return result;
}
private void testroutefail(string url)
{
// arrange
routecollection routes = new routecollection();
routeconfig.registerroutes(routes);
// act - process the route
routedata result = routes.getroutedata(createhttpcontext(url));
// assert
assert.istrue(result == null || result.route == null);
}
[testmethod]
public void testincomingroutes()
{
// check for the url that we hope to receive
testroutematch("~/admin/index", "admin", "index");
// check that the values are being obtained from the segments
testroutematch("~/one/two", "one", "two");
// ensure that too many or too few segments fails to match
testroutefail("~/admin/index/segment");//失败
testroutefail("~/admin");//失败
testroutematch("~/", "home", "index");
testroutematch("~/customer", "customer", "index");
testroutematch("~/customer/list", "customer", "list");
testroutefail("~/customer/list/all");//失败
testroutematch("~/customer/list/all", "customer", "list", new { id = "all" });
testroutematch("~/customer/list/all/delete", "customer", "list", new { id = "all", catchall = "delete" });
testroutematch("~/customer/list/all/delete/perm", "customer", "list", new { id = "all", catchall = "delete/perm" });
}
}
最后还是再推荐一下adam freeman写的apress.pro.asp.net.mvc.4这本书。稍微熟悉mvc的从第二部分开始读好了。前面都是入门(对我来说是扯淡)。但总比国内某些写书的人好吧——把个开源项目的源代码下载下来帖到书上面来,然后标题起个深入解析xxxx,然后净瞎扯淡。最后一千多页的巨著又诞生了。adam freeman的风格我就很喜欢,都是实例写作,然后还在那边书里面专门写了大量的测试。
哎没办法啊,技术差距就是这样了。
以上就是史上最全的asp.net mvc路由配置的详细内容。