上一主题下一主题
«123»Pages: 1/3     Go
关键字
主题 : ASP.NET MVC4 URL Routing 深入与使用URL最佳实践
级别: VIP四级

UID: 230365
精华: 12
发帖: 314
威望: 3432 点
学点: 380 点
贡献: 2 点
好评: 1 点
学币: 0 个
注册时间: 2010-04-16
最后登录: 2014-09-25
楼主  发表于: 2014-07-16 16:38||

0 ASP.NET MVC4 URL Routing 深入与使用URL最佳实践

管理提醒: 本帖被 zylzyl 设置为精华(2014-07-17)
&HXSO,@  
17i^|&J6}:  
PRz oLzr  
=!CuCV7$1O  
ASP.NET MV**** URL Routing 深入与使用URL最佳实践 I@S<D"af  
Fp]8f&l8  
'9/kDkt!  
目录: wKeqR$  
前言 {f&ga  
URL Routing 的定义方式 R]Yhuo9,&n  
示例准备 UKJY.W!w4  
给片段变量定义默认值 @iP6 N  
定义静态片段 8Cqs@<r4Od  
自定义片段变量 !xc7~D@om(  
路由约束 OX`n`+^D  
定义请求磁盘文件路由 Td  F<  
生成URL(链接) /< :; ^B  
URL方案最佳实践 \;6F-0  
$ vjmW! O  
内容: \fz j fZ1n  
1. 前言 0@Ijk(|  
我们知道在ASP.NET Web Forms中,一个URL请求往往对应一个aspx页面,一个aspx页面就是一个物理文件,它包含对请求的****。 c'B"Onu@m*  
而在ASP.NET MVC中,一个URL请求是由对应的一个Controller中的Action来****的,由URL Routing来告诉MVC如何定位到正确的Controller和Action。 E nvs[YZe  
总的来说,URL Routing包含两个主要功能:解析URL 和 生成URL,本文将围绕这两个大点进行讲解。 qC4-J)8 Wk  
2. URL Routing 的定义方式 ;gMh]$|"  
让我们从下面这样一个简单的URL****始: 84 [^#ke  
http://mysite.com/Admin/Index =e;wEf%`  
在域名的后面,默认使用“/”来对URL进行分段。路由系统通过类似于 {controller}/{action} 格式的字符串可以知道这个URL的 Admin 和 Index 两个片段分别对应Controller和Action的名称。 ~/U0S.C  
默认情况下,路由格式中用“/”分隔的段数是和URL域名的后面的段数是一致的,比如,对于{controller}/{action} 格式只会匹配两个片段。如下表所示: #~x5}8  
VL{#.;QQa  
URL路由是在MVC工程中的App_Start文件夹下的RouteConfig.cs文件中的RegisterRoutes方法中定义的,下面是创建一个空MVC项目时系统生成的一个简单URL路由定义: W*<]`U_.  
public static void RegisterRoutes(RouteCollection routes) { eFio,  
    routes.IgnoreRoute("{resource}.axd/{*pathInfo}"); t4IJ%#22  
    routes.MapRoute( }SV3PdE  
        name: "Default", ':V_V. :  
        url: "{controller}/{action}/{id}", RF)B4D-W  
        defaults: new { controller = "Home", action = "Index",  id = UrlParameter.Optional } *jK))|%  
    ); 1,j9(m2  
} %)9]dOdOk  
静态方法RegisterRoutes是在Global.asax.cs文件中的Application_Start方法中被调用的,除了URL路由的定义外,还包含其他的一些MVC核心特性的定义: ]CZLaID~  
protected void Application_Start() { S |x)7NC  
    AreaRegistration.RegisterAllAreas(); JC"K{ V{  
    WebApiConfig.Register(GlobalConfiguration.Configuration); Y*k<NeDyn  
    FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters); yU|ji?)e  
    RouteConfig.RegisterRoutes(RouteTable.Routes); ?i~/gjp  
    BundleConfig.RegisterBundles(BundleTable.Bundles); "Cc"y* P  
} *$(CiyF!  
RouteConfig.RegisterRoutes方法中传递的是 RouteTable 类的静态 Routes 属性,返回一个RouteCollection的实例。其实,“原始”的定义路由的方法可以这样写: 5uo(z,WLR  
public static void RegisterRoutes(RouteCollection routes) { d&bc>Vt  
DH9?2)aR  
    Route myRoute = new Route("{controller}/{action}", new MvcRouteHandler()); %-/:ps  
    routes.Add("MyRoute", myRoute);  -C  ON  
} %`QsX {?,  
创建Route对象时用了一个URL格式字符串和一个MvcRouteHandler对象作为构造函数的参数。不同的ASP.NET技术有不同的RouteHandler,MVC用的是MvcRouteHandler。 pMp9 O/u%  
这种写法有点繁琐,一种更简单的定义方法是: s|r7DdI  
public static void RegisterRoutes(RouteCollection routes) { XOg(k(&T  
    routes.MapRoute("MyRoute", "{controller}/{action}"); kQd[E-b7  
} |D`b7h  
这种方法简洁易读,一般我们都会用这种方法定义路由。 = )l:^+q  
3. 示例准备 k*3_) S -  
作为演示,我们先来准备一个Demo。创建一个标准的MVC应用程序,然后添加三个简单的Controller,分别是HomeController、CustomerController和AdminController,代码如下: pZ~> l=-  
public class HomeController : Controller { ,35: Srf|  
             }t D!xI;  
    public ActionResult Index() { 5m USh3  
        ViewBag.Controller = "Home"; w+9C/U;|s  
        ViewBag.Action = "Index"; h"1"h.  
        return View("ActionName"); |@L &yg,x  
    } bOSYr<R&  
} 2PP-0 E  
public class CustomerController : Controller { D0y,TF  
         ,gL)~6!A  
    public ActionResult Index() { iWN.3|r  
        ViewBag.Controller = "Customer"; <W=[ sWJ  
        ViewBag.Action = "Index"; ~?`9i>3W~  
        return View("ActionName"); ? W2I1HEy  
    } *Nf4bH%MN  
iZ.&q 6  
    public ActionResult List() { 7\0|`{|R@  
        ViewBag.Controller = "Customer"; U8WHE=Kk\h  
        ViewBag.Action = "List"; N1+4bR  
        return View("ActionName"); 6Y>,e;R  
    } 0XR;5kd%  
} @ yg| OA}  
public class AdminController : Controller { :_fjml/  
         m|nL!Wc  
    public ActionResult Index() { JI+KS  
        ViewBag.Controller = "Admin"; L.IoGUxD  
        ViewBag.Action = "Index"; e_I; y  
        return View("ActionName"); Z0()pT  
    } Xc8= 2n  
} NRHr6!f>  
在 /Views/Shared 文件夹下再给这三个Controller添加一个共享的名为 ActionName.cshtml 的 View,代码如下: so?pA@O  
@{ Xb(CH#*{z  
    Layout = null;  l5ZADK4  
} [ 5CS}FB  
<!DOCTYPE html> QQ^Gd8nQ  
<html> K;<NBnH  
<head> 3iu!6lC  
    <meta name="viewport" content="width=device-width" /> A0cM(w{7_  
    <title>ActionName</title> fs2m N1  
</head> T7>4 8eH  
<body> o|z +!,  
    <div>The controller is: @ViewBag.Controller</div> E/"YId `A  
    <div>The action is: @ViewBag.Action</div> 9nT?|n]>  
</body> <u6c2!I{  
</html> f|- m ^/y  
我们把RouteConfig.cs文件中项目自动生成的URL Rounting的定义****了,然后根据前面讲的路由定义知识,我们自己写一个最简单的: X4d Xm>*?=  
public static void RegisterRoutes(RouteCollection routes) { GwgFi@itN  
    routes.MapRoute("MyRoute", "{controller}/{action}"); I["F+kt^^  
} 9T8|y]0F  
程序运行,URL定位到 Admin/Index 看看运行结果: ,]0S4h67  
L@7Qs6G2u  
这个Demo输出的是被调用的Controller和Action名称。 ~Y\QGuT  
4 给片段变量定义默认值 R ^HohB  
在上面我们必须把URL定位到特定Controller和Action,否则程序会报错,因为MVC不知道去执行哪个Action。 我们可以通过指定默认值来告诉MVC当URL没有给出对应的片段时使用某个默认的值。如下给controller和action指定默认值: IM@"AD52a  
routes.MapRoute("MyRoute", "{controller}/{action}",  new { controller = "Home", action = "Index" }); +2- qlU  
这时候如果在URL中不****action片段的值或不****controller和action两个片段的值,MVC将使用路由定义中****的默认值: 3Z_t%J5QZ$  
[m~b[ZwES  
它的各种匹配情况如下表所示: Bk&-1>cY  
:_>\DJ'>  
注意,对于上面的URL路由的定义,我们可以只给action一个片段指定默认值,但是不能只给controller一个片段指定默认值,即如果我们给Controller指定了默认值,就一定也要给action指定默认值,否则URL只有一个片段时,这个片段匹配给了controller,action将找不到匹配。 h8em\<;  
5 定义静态片段 l+t #"3  
并不是所有的片段都是用来作为匹配变量的,比如,我们想要URL加上一个名为Public的固定前缀,那么我们可以这样定义: :lNg:r$4  
routes.MapRoute("", "Public/{controller}/{action}",  new { controller = "Home", action = "Index" }); +-U@0&Y3M  
这样,请求的URL也需要一个Public前缀与之匹配。我们也可以把静态的字符串放在大括号以外的任何位置,如: M|ms$1x  
routes.MapRoute("", "X{controller}/{action}",  new { controller = "Home", action = "Index" }); Y=9qJ`q  
在一些情况下这种定义非常有用。比如当你的网站某个链接已经被用户普遍记住了,但这一块功能已经有了一个新的版本,但调用的是不同名称的controller,那么你把原来的controller名称作为现在controller的别名。这样,用户依然使用他们记住的URL,而导向的却是新的controller。如下使用Shop作为Home的一个别名: o\&~CW~@~  
routes.MapRoute("ShopSchema", "Shop/{action}",  new { controller = "Home" }); ~xw 5\Y^  
这样,用户使用原来的URL可以访问新的controller: 2%5?F n=  
%06vgjOa (  
6 自定义片段变量 >'N!dM.+9  
自定义片段变量的定义和取值 l^ARW E  
contrlloer和action片段变量对MVC来说有着特殊的意义,在定义一个路由时,我们必须有这样一个概念:contrlloer和action的变量值要么能从URL中匹配得到,要么由默认值****,总之一个URL请求经过路由系统交给MVC****时必须保证contrlloer和action两个变量的值都有。当然,除了这两个重要的片段变量,我们也可从通过自定义片段变量来从URL中得到我们想要的其它信息。如下自定义了一个名为Id的片段变量,而且给它定义了默认值: eI0F!Yon  
routes.MapRoute("MyRoute", "{controller}/{action}/{id}", jM]d'E?ZLA  
    new{ Gbn4 *<N  
        controller = "Home", jP+ pA e  
        action = "Index", {M [~E|@D  
        id = "DefaultId"}); * MEe,4  
}); ]rnXNn;  
我们在HomeController中增加一个名为CustomVariable的ACtion来演示一下如何取自定义的片段变量: +RS$5NLH  
publicActionResult CustomVariable() { 9_UN.]  
    ViewBag.Controller = "Home"; {OCJ(^8i  
    ViewBag.Action = "CustomVariable"; ETO$9}x[  
    ViewBag.CustomVariable = RouteData.Values["id"]; > ws!5q  
    return View("ActionName"); #9\THfb  
} 4-V)_U#8  
可以通过 RouteData.Values[segment] 来取得任意一个片段的变量值。 Bc<n2 C0  
再稍稍改一下ActionName.cshtml 来看一下我们取到的自定义片段变量的值: /c#l9&,  
... \ I:.<2i  
<div>The controller is: @ViewBag.Controller</div> 2+:'0Krc  
<div>The action is: @ViewBag.Action</div> g<a<*)&  
<div>The custom variable is: @ViewBag.CustomVariable</div>... qfr Ni1\9-  
将URL定位到 /Home/CustomVariable/Hello 将得到如下结果: 6t7fa<  
8TPN#"  
自定义的片段变量用处很大,也很灵活,下面介绍一些常见的用法。 v{y{sA  
7 将自定义片段变量作为Action方法的参数 S4bBafj[I  
我们可以将自定义的片段变量当作参数传递给Action方法,如下所示: `R+,1"5=  
public ActionResult CustomVariable(string id) { $iPP|Rw  
    ViewBag.Controller = "Home"; re`t ]gzb  
    ViewBag.Action = "CustomVariable"; hO( RZ '{  
    ViewBag.CustomVariable =id; cj g.lzY H  
    return View("ActionName"); E FY@Y[  
} A 5nO=  
效果和上面是一样的,只不过这样省去了用 RouteData.Values[segment] 的方式取自定义片段变量的麻烦。这个操作背后是由模型绑定来****的 [BDGR B7d"  
8 指定自定义片段变量为可选 _^\$" nw  
指定自定片段变量为可选,即在URL中可以不用指定片段的值。如下面的定义将Id定义为可选: L`[z[p {?  
routes.MapRoute("MyRoute", "{controller}/{action}/{id}", new { xs!p|  
        controller = "Home", z$[C#5+2  
        action = "Index", r;gP}H ?  
        id=UrlParameter.Optional Zyy e%Ly  
}); kWW w<c A  
定义为可选以后,需要对URL中没有Id这个片段值的情况进行****,如下: @i%YNI5*  
public ActionResult CustomVariable(string id) { i8PuC^]  
    ViewBag.Controller = "Home"; WsOi,oG@  
    ViewBag.Action = "CustomVariable"; $_X|, v 9  
    ViewBag.CustomVariable = id == null ? "<no value>" : id; @pza>^wk  
    return View("ActionName"); L,]=vba'$  
} K_/B?h  
当Id是整型的时候,参数的类型需要改成可空的整型(即int? id)。 )TmtS SS  
为了省去判断参数是否为空,我们也可以把Action方法的id参数也定义为可选,当没有****Id参数时,Id使用默认值,如下所示: -J0WUN$2*  
public ActionResult CustomVariable(string id = "DefaultId") { HOi C  
    ViewBag.Controller = "Home"; lmCZ8 j(FF  
    ViewBag.Action = "CustomVariable"; z1V#'$_5-  
    ViewBag.CustomVariable = id; V'gw\mcb  
    return View("ActionName"); 5g ;ac~g  
} ANMg  
这样其实就是和使用下面这样的方式定义路由是一样的: bKbp?-]  
routes.MapRoute("MyRoute", "{controller}/{action}/{id}", new { controller = "Home", action = "Index", id = "DefaultId" }); ` %?9=h%  
9 定义可变数量的自定义片段变量 XhW %,/<  
我们可以通过 catchall 片段变量加 * 号前缀来定义匹配任意数量片段的路由。如下所示: 3Rv7Qx  
routes.MapRoute("MyRoute", "{controller}/{action}/{id}/{*catchall}", dZ(|uC!?  
new { controller = "Home", action = "Index",  id = UrlParameter.Optional }); &xS a7FY  
这个路由定义的匹配情况如下所示: =tdSq"jh  
9s$U%F6}  
使用*catchall,将匹配的任意数量的片段,但我们需要自己通过“/”分隔catchall变量的值来取得独立的片段值。 r.3KPiYK  
10 路由约束 1(dj[3Mt  
正则表达式约束 `iG,H[t+j  
通过正则表达式,我们可以制定限制URL的路由规则,下面的路由定义限制了controller片段的变量值必须以 H 打头: (M-ZQ -  
我们可以用正则表达式约束来定义只有指定的几个特定的片段值才能进行匹配,如下所示: v> z@  
routes.MapRoute("MyRoute", "{controller}/{action}/{id}", v:_B kHN'  
    new { controller = "Home", action = "Index", id = UrlParameter.Optional }, Es5p}uh.[Y  
    new { controller = "^H.*", action = "^Index$|^About$" } zsl,,gk9Y  
); P>Qpv Sd_#  
这个定义,限制了只能访问controller以H打着的,action片段值只能是Index或About,不区分大小写。  -7]Xjb5  
Http请求方式约束 hsLzj\)6  
我们还可以限制路由只有当以某个特定的Http请求方式才能匹配。如下限制了只能是Get请求才能进行匹配: W  0[N0c  
routes.MapRoute("MyRoute", "{controller}/{action}/{id}", Y}R}-+bD/  
    new { controller = "Home", action = "Index", id = UrlParameter.Optional }, r4 dOK] 0  
    new { controller = "^H.*", httpMethod = new HttpMethodConstraint("GET") } |FR3w0o  
); d.<~&.-$  
通过创建一个 HttpMethodConstraint 类的实例来定义一个Http请求方式约束,构造函数传递是允许匹配的Http方法名。这里的httpMethod属性名不****娑ǖ模皇俏饲帧 XR+3j/zEQ  
这种约束也可以通过HttpGet或HttpPost过滤器来实现。 -#&kYK#Ph  
自定义路由约束 5"Kx9n|  
如果标准的路由约束满足不了你的需求,那么可以通过实现 IRouteConstraint 接口来定义自己的路由约束规则。 $A\m>*@  
我们来****一个限制浏览器版本访问的路由约束。在MVC工程中添加一个文件夹,取名Infrastructure,然后添加一个 UserAgentConstraint 类文件,代码如下: V1=*z  
public class UserAgentConstraint : IRouteConstraint { ;."<m   
         /M0/-pV 9  
    private string requiredUserAgent; eOl KbJU  
p QE)p  
    public UserAgentConstraint(string agentParam) { Eh8Pwt7C@  
        requiredUserAgent = agentParam; JHV)ZOO  
    } D@Da0  
0Dv JZ|e  
    public bool Match(HttpContextBase httpContext, Route route, string parameterName, JyBp-ii  
        RouteValueDictionary values, RouteDirection routeDirection) { Ibr%d2yS=  
             x4?10f(9=  
        return httpContext.Request.UserAgent != null ~9o6 W",  
            && httpContext.Request.UserAgent.Contains(requiredUserAgent); E$\~lcq  
    } =jAFgwP\  
} E0f{iO;}  
这里实现IRouteConstraint的Match方法,返回的bool值告诉路由系统请求是否满足自定义的约束规则。我们的UserAgentConstraint类的构造函数接****一个浏览器名称的关键字作为参数,如果用户的浏览器包含****的关键字才可以访问。接一来,我们需要****自定的路由约束: +a)E|(cN  
public static void RegisterRoutes(RouteCollection routes) { +Ghi}v  
d.F)9h]XHO  
    routes.MapRoute("ChromeRoute", "{*catchall}", $^%N U  
        new { controller = "Home", action = "Index"}, |Ur"za;%@  
        new { customConstraint = new UserAgentConstraint("Chrome") } **;p (CI  
    ); HMQI&Lh=U  
} 4YA./j%'  
下面分别是IE10和Chrome浏览器请求的结果: %_)zWl N  
a:!uORQby  
11 定义请求磁盘文件路由 H oy7RC&  
并不是所有的URL都是请求controller和action的。有时我们还需要请求一些资源文件,如图片、html文件和JS库等。 n Y w\'c  
我们先来看看能不能直接请求一个静态Html文件。在项目的Content文件夹下,添加一个html文件,内容随意。然后把URL定位到该文件,如下图: z/|BH^Vw  
[img]http://bbs.ibeifeng.com/attachment/637_459674_42215b5d1cf9e27.png[/img 2<W&\D o@  
我们看到,是可以直接访问一静态资源文件的。 'CV^M(o'9  
默认情况下,路由系统先检查URL是不是请求静态文件的,如果是,**器直接返回文件内容并结束对URL的路由解析。我们可以通过设置 RouteCollection的 RouteExistingFiles 属性值为true 让路由系统对静态文件也进行路由匹配,如下所示: " ~Q*XN2  
public static void RegisterRoutes(RouteCollection routes) { x7ATI[b[  
     <0j{ $.  
    routes.RouteExistingFiles = true; Zw JciT!_~  
McP.9v}H0_  
    routes.MapRoute("MyRoute", "{controller}/{action}/{id}/{*catchall}", oXal  
        new { controller = "Home", action = "Index", id = UrlParameter.Optional 3E|;r _; 8  
    }); 2P}I'4C-  
} fPQ|e"?  
设置了routes.RouteExistingFiles = true后,还需要对IIS进行设置,这里我们以IIS Express为例,右键IIS Express小图标,选择“显示所有应用程序”,弹出如下窗口: '`o[+.  
[ b1hC ~I;  
点击并打****配置文件,Control+F找到UrlRoutingModule-4.0,将这个节点的preCondition属性改为空,如下所示: "iek,Y}j7  
<add name="UrlRoutingModule-4.0" type="System.Web.Routing.UrlRoutingModule" preCondition=""/> "$@,n7 k  
然后我们运行程序,再把URL定位到之前的静态文件: &o.iUk  
K-@bwB7~s  
这样,路由系统通过定义的路由去匹配RUL,如果路由中没有定义该静态文件的匹配,则会报上面的错误。 *8-p7,D  
一旦定义了routes.RouteExistingFiles = true,我们就要为静态文件定义路由,如下所示: F 6&P~H  
public static void RegisterRoutes(RouteCollection routes) { `o-*Tr  
     ND,`QjmZ  
    routes.RouteExistingFiles = true; 6/7F">@j  
Q X):T#^V  
    routes.MapRoute("DiskFile", "Content/StaticContent.html", XGk}e4;_  
        new { controller = "Customer", action = "List", }); g RU- g  
88YC0!Ni  
    routes.MapRoute("MyRoute", "{controller}/{action}/{id}/{*catchall}", o16~l]Z|f  
        new { controller = "Home", action = "Index", id = UrlParameter.Optional }); kA,4$ 2_o  
} Ou!)1UFI  
这个路由匹配Content/StaticContent.html的URL请求为controller = Customer, action = List。我们来看看运行结果: d~/xGB`<  
dcP88!#5-  
这样****的目的是为了可以在Controller的Action中控制对静态资源的请求,并且可以阻止对一些特殊资源文件的访问。 c }'Xoc  
设置了RouteExistingFiles属性为true后,我们要为允许用户请求的资源文件进行路由定义,如果每种资源文件都去定义相应的路由,就会显得很繁琐。 8E=vR 8  
我们可以通过RouteCollection类的IgnoreRoute方法绕过路由定义,使得某些特定的静态文件可以由**器直接返回给给浏览器,如下所示: 8_Jj+  
public static voidRegisterRoutes(RouteCollection routes) { lXw;|dGF  
     (E IRz>  
    routes.RouteExistingFiles = true; ~Ji>[#W K  
L*IU0Jy>  
    routes.IgnoreRoute("Content/{filename}.html"); QdirE4W  
69-$Wn43<  
    routes.MapRoute("DiskFile", "Content/StaticContent.html", zK,~37)\  
        new { controller = "Customer", action = "List", }); M$#+W?m&  
$=7H1 w  
    routes.MapRoute("MyRoute", "{controller}/{action}/{id}/{*catchall}", Hc>([?P%t  
        new { controller = "Home", action = "Index", id = UrlParameter.Optional }); p-a]"l+L  
} B>R* f C@g  
这样,只要是请求Content目录下的任何html文件都能被直接返回。这里的IgnoreRoute方法将创建一个RouteCollection的实例,这个实例的Route Handler 为 StopRoutingHandler,而不是 MvcRouteHandler。运行程序定位到Content/StaticContent.html,我们又看到了之前的静态面面了。 ZjE~W>pkQ  
12 生成URL(链接) LZbRQ"!!o  
前面讲的都是解析URL的部分,现在我们来看看如何通过路由系统在View中生成URL。 #cR57=M}  
生成指向当前controller的action链接 Ua}g  
在View中生成URL的最简单方法就是调用Html.ActionLink方法,如下面在 Views/Shared/ActionName.cshtml 中的代码所示: K,Lr +  
... (q055y  
<div>The controller is: @ViewBag.Controller</div> B[C7G7<B  
<div>The action is: @ViewBag.Action</div> rD_Ss.\^g  
<div>    @Html.ActionLink("This is an outgoing URL", "CustomVariable") r7zf+a]  
</div>... \xy:6gd:  
这里的Html.ActionLink方法将会生成指向View对应的Controller和第二个参数指定的Action,我们可以看看运行后页面是如何显示的: a g|9$  
=iA"; x  
经过查看Html源码,我们发现它生成了下面这样的一个html链接: jJiCF,m  
<a href="/Home/CustomVariable">This is an outgoing URL</a> <e@I1iL37y  
这样看起来,通过Html.ActionLink生成URL似乎并没有直接在View中自己写一个<a>标签更直接明了。 但它的好处是,它会自动根据路由配置来生成URL,比如我们要生成一个指向HomeContrller中的CustomVariable Action的连接,通过Html.ActionLink方法,只需要给出对应的Controller和Action名称就行,我们不需要关心实际的URL是如何组织的。举个例子,我们定义了下面的路由: x'VeL|  
public static void RegisterRoutes(RouteCollection routes) { 1 8l~4"|fk  
             vq@#Be?@  
    routes.MapRoute("NewRoute", "App/Do{action}", new { controller = "Home"}); O :5ldI  
              uhPIV\  
    routes.MapRoute("MyRoute", "{controller}/{action}/{id}", Br?++ \  
        new { controller = "Home", action = "Index", id = UrlParameter.Optional }); /0'fcjOaQ  
} >QbI)if`1  
运行程序,我们发现它会自动生成下面这样的连接: c;A ew!  
<a href="/App/DoCustomVariable">This is an outgoing URL</a> pw(*X,gj  
所以我们要生成指向某个Action的链接时,最好使用Html.ActionLink方法,否则你很难保证你手写的连接就能定位到你想要的Action。 vM$#m1L?  
生成其他controller的action链接 ]Y/pSwnV  
上面我们给Html.ActionLink方法传递的第二个参数只告诉了路由系统要定位到当前View对应的Controller下的Action。Html.ActionLink方法可以使用第三个参数来指定其他的Controller,如下所示: w^vK7Z 1$  
<div> Sxx.>gP"61  
    @Html.ActionLink("This targets another controller", "Index", "Admin") t2U]CI%  
</div> x<j($iv  
它会自动生成如下链接: +=lcN~U2  
<a href="/Admin">This targets another controller</a> y0Q/B|&[  
生成带有URL参数的链接 N NXwT0t  
有时候我们想在连接后面加上参数以传递数据,如 ?id=xxx 。那么我们可以给Html.ActionLink方法指定一个匿名类型的参数,如下所示: L< nkI  
<div>   @Html.ActionLink("This is an outgoing URL", "CustomVariable", new { id = "Hello" }) fuwpp  
</div> /m|&nl8"qe  
它生成的Html如下: wr6xuoH  
<a href="/Home/CustomVariable/Hello">This is an outgoing URL</a> t+Q|l&|0  
指定链接的Html属性 8;dbU*  
通过Html.ActionLink方法生成的链接是一个a标签,我们可以在方法的参数中给标签指定Html属性,如下所示: (2a "W`  
<div> d$C|hT  
    @Html.ActionLink("This is an outgoing URL",  "Index", "Home", null, ():?FJ M  
        new {id = "myAnchorID", @class = "myCSSClass"}) "/]tFY%Y  
</div> kl<B*:RqH  
这里的class加了@符号,是因为class是C#关键字,@符号起到转义的作用。它生成 的Html代码如下: 9/qS*Zdh)  
<a class="myCSSClass" href="/"id="myAnchorID">This is an outgoing URL</a> F/,<dNJ  
生成完整的标准链接 &+|4(d1  
前面的都是生成相对路径的URL链接,我们也可以通过Html.ActionLink方法生成完整的标准链接,方法如下: vrEaNT$J-  
<div> [LEh  
    @Html.ActionLink("This is an outgoing URL", "Index", "Home", GP k Cgb(  
        "https", "myserver.mydomain.com", " myFragmentName", 9^h%}>  
        new { id = "MyId"}, h+d;`7Z>  
        new { id = "myAnchorID", @class = "myCSSClass"}) 7%x+7  
</div> Br$PL&e~  
这是Html.ActionLink方法中最多参数的重载方法,它允许我们****请求的协议(https)和目标**器地址(myserver.mydomain.com)等。它生成的链接如下: }4ju2K  
<a class="myCSSClass" id="myAnchorID"    href="https://myserver.mydomain.com/Home/Index/MyId#myFragmentName" >    This is an outgoing URL</a> ~O7(0RsCN  
生成URL字符串 s7.*o@G  
用Html.ActionLink方法生成一个html链接是非常有用而常见的,如果要生成URL字符串(而不是一个Html链接),我们可以用 Url.Action 方法,使用方法如下: NKh {iSLm  
<div>This is a URL: ;l4 \^E1  
    @Url.Action("Index", "Home", new { id = "MyId" }) y$Noo)Z  
</div> "{xv|C<*n  
它显示到页面是这样的: h4i $z-!  
6EWB3.x19  
根据指定的路由名称生成URL Czxrn2p/  
我们可以根据某个特定的路由来生成我们想要的URL,为了更好说明这一点,下面给出两个URL的定义:  (t['  
public static void RegisterRoutes(RouteCollection routes) { ay|{!MkQ  
    routes.MapRoute("MyRoute", "{controller}/{action}"); i?=.; 0[|  
    routes.MapRoute("MyOtherRoute", "App/{controller}/{action}", new { controller = "Home" }); 28yxX431S  
} @&B!P3{f  
对于这样的两个路由,对于类似下面这样的写法: xB+H7Ya  
@Html.ActionLink("Click me", "Index", "Customer") (W!$6+GT  
始终会生成这样的链接: lM"7 Z  
<a href="/Customer/Index">Click me</a> RBD MZ  
也就是说,永远无法使用第二个路由来生成App前缀的链接。这时候我们需要通过另一个方法Html.RouteLink来生成URL了,方法如下: ;P~S/j[ 8  
  @Html.RouteLink("Click me", "MyOtherRoute", new {controller="Customer",action="Index" }); 3Q)>gh*  
它会生成如下链接: Ti'kn{ Zv  
<a Length="8" href="/App/Customer/Index">Click me</a> j56 An6g  
这个链接指向的是HomeController下的Index Action。但需要注意,通过这种方式来生成URL是不****的,因为它不能让我们从直观上看到它生成的URL指向的controller和action。所以,非到万不得已的情况才会这样用。 xvl  
在Action方法中生成URL $(=1A>40  
通常我们一般在View中才会去生成URL,但也有时候我们需要在Action中生成URL,方法如下: ): C4}&l  
Public ViewResult MyActionMethod() { tvRy8u;  
     ;i>E @  
    string myActionUrl = Url.Action("Index", new { id = "MyID" }); id'# s  
    string myRouteUrl = Url.RouteUrl(new { controller = "Home", action = "Index" }); pN_!&#|+$  
     e]?S-J'z  
    //... do something with URLs... ,O$C9pH9  
    return View(); K~T\q_ZPZ  
} *`T &Dlt'8  
其中 myActionUrl 和 myRouteUrl 将会被分别赋值 /Home/Index/MyID 和 / 。 vy2aNUmt  
更多时候我们会在Action方法中将客户端浏览器重定向到别的URL,这时候我们使用RedirectToAction方法,如下: H%aLkV!J  
public RedirectToRouteResultMyActionMethod() { " SqKS,J  
    return RedirectToAction("Index"); ;jI"|v{vnS  
} >nEnX  
RedirectToAction的返回结果是一个RedirectToRouteResult类型,它使MVC触发一个重定向行为,并调用指定的Action方法。RedirectToAction也有一些重载方法,可以传入controller等信息。也可以使用RedirectToRoute方法,该方法传入的是object匿名类型,易读性强,如: C/ VYu-p%  
publicRedirectToRouteResult MyActionMethod() { RNtA4rC>#  
    return RedirectToRoute(new { controller = "Home", action = "Index", id = "MyID"}); *Bsmn!_cB{  
} v=-3 ,C  
7#qL9+G  
13 URL方案最佳实践 N2s%p6RMPD  
){6)?[G  
下面是一些使用URL的建议: D#}Yx]Q1  
最好能直观的看出URL的意义,不要用应用程序的具体信息来定义URL。比如使用 /Articles/Report 比使用 /Website_v2/CachedContentServer/FromCache/Report 好。 [j U  
?QT"sj64w  
翰用内容标题比使用ID好。比如使用 /Articles/AnnualReport 比使用 /Articles/2392 好。如果一定要使用使用ID(比如有时候可能需要区分相同的标题),那么就两者都用,如 /Articles/2392/AnnualReport ,它看起来很长,但对用户更友好,而且更利于SEO。 i/~QJ1C  
H<7DcwXv  
对于Web页面不要使用文件扩展名(如 .aspx 或 .mvc)。但对于特殊的文件使用扩展名(如 .jpg、.pdf 和 .zip等)。 rtmt 3  
Cf=q_\0|W  
尽可能使用层级关系的URL,如 /Products/Menswear/Shirts/Red,这样用户就能猜到父级URL。 *u;">H*BW  
[*{\R`M  
不区分大小写,这样方便用户输入。 KSB_%OI1  
Bx : So6:  
揰湫罸用Get和Post。Get一般用来从**器获取只读的信息,当需要操作更改状态时使用Post。 w*xUuwi  
hN:F8r+DG  
尽可能避免使用标记符号、代码、字符序列等。如果你想要用标记进行分隔,就使用中划线(如 /my-great-article),下划线是不友好的,另外空格和+号都会被URL编码。 H;*a:tbxO+  
KWh M  
不要轻易改变URL,尤其对于互联网网站。如果一定要改,那也要尽可能长的时间保留原来的URL。 7?!Z+r  
<.=#EV^i  
尽量让URL使用统一的风格或习惯。 60aKT:KLC_  
注以上内容来自于:《Pro ASP.NET MVC 4 4th Edition》 cR=94i=t  
5h|m 4)$  
附:RouterDebugger安装使用说明 : n,?IcDU~m  
1.安装 6_%]\37_Z  
在程序包控制台中执行命令 &8Jg9#  
PM> Install-Package routedebugger Wm$`ae   
安装成功后Web.config文件中会自动加入行 '6o`^u>  
<add key="RouteDebugger:Enabled"value="true" /> s^g.42?u  
2.使用 mWli}j#  
MVC3中需要在文件Global.asax中 Application_Start() 中添加代码 PreApplicationStart.Start();  MV**** 以后不需要 so!w!O@@  
如下样式: ]>)u+|  
protected voidApplication_Start() { e8AjO$49  
RegisterRoutes(RouteTable.Routes); z/ Ns5  
PreApplicationStart.Start(); //....... } ceb s.sF:  
3.禁用 7b&JX'`Mb  
web.config中 't|Un G  
<add key="RouteDebugger:Enabled" value="true" /> TvunjTpaj  
改为 j{{~ZM  
<add key="RouteDebugger:Enabled" value="false" /> PTbA1.B  
4.运行效果 ~!]&>n;=G  
PPoI>J  
RouteDebuger见附件:链接:http://pan.baidu.com/s/1jGqTMXS I*A0?{  
密码:
本部分内容设定了隐藏,需要回复后才能看到
?$|uT  
此课程系 我本人的大型商业课程《基于ASP.NET MVC 4 +Knockout.JS+Web API+FluentData+EasyUI 技术实现Web通用商业****发框架》中 项目预备知识部分的 jl.okWuiY  
课程四《ASP.NET MV**** 核心技术教程》中的《专题三、实例快速上手 - ASP.NET MV**** URL Routing 深入与使用URL最佳实践》 bA@ /B'  
整套课程预计7月份内上线预****,敬请关注北风官网! w]>"'o{{  
此章节视频目录如下: M.ZEqV+k  
;wij}y-6  
此处公****前两讲,需要更多资源的同学可以给我留言 [3O^0-:6E  
0U82f1ei  
W@I 02n2 H  
uiktdZ/f  
级别: 北风爱好者

UID: 476548
精华: 0
发帖: 3
威望: 6 点
学点: 1 点
贡献: 0 点
好评: 0 点
学币: 0 个
注册时间: 2014-07-18
最后登录: 2016-11-21
沙发(1楼)  发表于: 2014-07-18 11:43||

别搞得那么经典好不
级别: VIP五级

UID: 270178
精华: 0
发帖: 21
威望: 449 点
学点: 491 点
贡献: 48 点
好评: 0 点
学币: 8 个
注册时间: 2010-11-11
最后登录: 2018-06-07
板凳(2楼)  发表于: 2014-07-22 10:55||

期待MV****+EasyUI+FluentData的新课程!
级别: 北风爱好者

UID: 493021
精华: 0
发帖: 5
威望: 5 点
学点: 0 点
贡献: 0 点
好评: 0 点
学币: 0 个
注册时间: 2014-08-24
最后登录: 2014-08-24
地板(3楼)  发表于: 2014-08-24 23:11||

点赞!!
级别: 北风爱好者

UID: 493021
精华: 0
发帖: 5
威望: 5 点
学点: 0 点
贡献: 0 点
好评: 0 点
学币: 0 个
注册时间: 2014-08-24
最后登录: 2014-08-24
地下室(4楼)  发表于: 2014-08-24 23:16||

感谢分享
级别: VIP三级

UID: 90325
精华: 0
发帖: 3
威望: 2 点
学点: 11 点
贡献: 0 点
好评: 0 点
学币: 0 个
注册时间: 2008-12-07
最后登录: 2016-11-11
下水道(5楼)  发表于: 2014-09-12 15:16||

吐血支持
级别: 北风爱好者

UID: 495644
精华: 0
发帖: 3
威望: 29 点
学点: 36 点
贡献: 3 点
好评: 0 点
学币: 1 个
注册时间: 2014-08-30
最后登录: 2014-09-12
6楼  发表于: 2014-09-12 21:34||

前来认真学习一下了。
级别: 北风工程师

UID: 470673
精华: 0
发帖: 3
威望: 2297 点
学点: 2316 点
贡献: 251 点
好评: 0 点
学币: 19 个
注册时间: 2014-06-29
最后登录: 2015-06-06
7楼  发表于: 2014-09-13 10:42||

好东西值得学习
级别: 北风技术菜鸟

UID: 125044
精华: 0
发帖: 13
威望: 37 点
学点: 36 点
贡献: 2 点
好评: 0 点
学币: 1 个
注册时间: 2009-02-25
最后登录: 2014-09-13
8楼  发表于: 2014-09-13 15:02||

.......
级别: VIP四级

UID: 460455
精华: 0
发帖: 4
威望: 4 点
学点: 13 点
贡献: 0 点
好评: 0 点
学币: 0 个
注册时间: 2014-04-08
最后登录: 2014-10-17
9楼  发表于: 2014-09-29 16:51||

期待课程
上一主题下一主题
«123»Pages: 1/3     Go