您的位置:首页 > 编程语言 > ASP

Urls, Routing and Area in Asp.net MVC

2012-10-20 22:50 609 查看
本文着重讲述Asp.net MVC的路由配置,url灵活动态输出以及Area的使用。

一、路由配置:这里忽略VS2010给默认生成的Route,按照先易后难的顺序来讲解。

在讲解route之前,先说一下url segment的概念。如对于url:http://mydomain.com/admin/index 它的segment有2个。第一个是admin,第二个是index。基于segment,关于Route匹配规则,有3点特征:1)保守,它只匹配包含相同个数segment的url(Route配置中有默认值或者是optional的例外);2)开明,只要segment个数相同,它就接纳(有特殊constraint配置除外);3)知足,只要一个url匹配了前边的Route,后面的就不在去匹配。下面是示例:

1. 最简单的做法是:

Route myRoute = new Route("{controller}/{action}", new MvcRouteHandler());
routes.Add("myRoute", myRoute); 当然,相同作用但更常用的写法是:

routes.MapRoute("myRoute", "{controller}/{action}");这里myRoute参数是optional。 下边讲解一些更实际的应用。

2. 定义默认值:

routes.MapRoute("defaultValueRoute", "{controller}/{action}", new { controller = "Home", action = "Index" });对于上述route配置,http://mydomain.com、http://mydomain.com/customer、http://mydomain.com/customer/list均能匹配上,当controller和action对应的segment部分没有指定时,采用默认值(Home、action),否则采用匹配的segment值。但 http://mydomain.com/customer/list/all 不行,因为它的url segment多于route配置的2个。

3. 包含静态url片段:

routes.MapRoute("staticFolderRoute", "Public/{controller}/{action}");它要求url有3个segment,并且第一部分必须是public。如:http://mydomain.com/public/customer/list可以匹配,但http://mydomain.com/customer/list/all不行。同时,请注意:public将不会保存在RouteData中。

除了将静态片段前置外,还可以将它混合在route配置中:

routes.MapRoute("mixedSegmentRoute", "X{controller}/{action}");可以看到,X和controller混在一起,它所匹配的url的第一片段必须以X开头,如:
http://mydomain.com/xcustomer/list。在RouteData中controller的值当然也就不包含X了。
虽然controller和action是2个内置的route key,并且一般情况下它们并存于route配置中。但是它们并不是必须都存在于route的url配置规则中,如下:

routes.MapRoute("fakeControllerRoute", "Shop/{action}", new { controller = "Home" });将controller直接置死为Home。 而根据示例的配置,发散一下可做它用。试想一下:如果站点的某个action(假设为OldAction)不再被使用了,而它所对应的url已被搜索引擎收录或者不想让它404,这个时候除了可以让action跳转走,还可以通过设置route:

routes.MapRoute("NolongerUsedActionRoute", "Shop/OldAction", new { controller = "Home", action = "Index" });后,再次访问它时,就直接映射到了Home/Index了,但是url然为Shop/OldAction。

4. 定义custom片段:

routes.MapRoute("CustomerVariableRoute", "{controller}/{action}/{id}", new { controller = "Home", action = "Index", id = "default id" });所谓的custom片段,就是除了controller和action的其他的那些,如上文的id。

5. optional片段:

routes.MapRoute("OptionalSegmentRoute", "{controller}/{action}/{id}", new { controller = "Home", action = "Index", id = UrlParameter.Optional });特别之处在于http://mydomain.com/public/customer虽然只包含2个segment,但是它可以匹配上述route的。同时,optional segment和default value segment区别的一点在于,同一url(如http://mydomain.com/public/customer)对于前者,RouteData["id"]是不存在的即RouteData中压根就没有id这一个key,后对于后者它是有的,只不过value为默认值。

6. segment数可变的route:

routes.MapRoute("CustomVariableLengthRoute", "{controller}/{action}/{id}/{*catchall}", new { controller = "Home", action = "Index", id = UrlParameter.Optional });该segment配置以*开头。http://mydomain.com/Customer, http://mydomain.com/Customer/List/All/Delete/Perm均能匹配上它,前者RouteData对应值为null,后者为Delete/Perm。
7. 在route中使用namespace:

Route系统默认情况下,只核对controller和action的名称,而对它们所在的namespace则视而不见。当项目中有相同的controller和action名称(如应用到Area时),此时若一个url刚好经过某个route匹配上它,则会报错,因为它不知道采用哪一对。这时就可以去设置核对的优先级了:

routes.MapRoute("PrioritizeNamespaceRoute", "{controller}/{action}/{id}/{*catchall}",
new { controller = "Home", action = "Index", id = UrlParameter.Optional },
new[] { "URLsAndRoutes.Controllers" });它会优先去从namespace:URLsAndRoutes.Controllers下去搜寻指定的controller和action,如果没有匹配上再去从别的namespace。

注意,MapRoute方法最后那个参数虽然是数组,但实质上如果你设置了多个namespace,而敲好又有多个有相同名称的controller和action,那么仍然会报相同的异常,因为数组参数中的namespace它们之间又完全是平等的,没有优先级。解决方法是设置多个route,如下:

routes.MapRoute("PrioritizeNamespaceRoute", "{controller}/{action}/{id}/{*catchall}",
new { controller = "Home", action = "Index", id = UrlParameter.Optional },
new[] { "URLsAndRoutes.Controllers" });
routes.MapRoute("PrioritizeNamespaceRoute", "{controller}/{action}/{id}/{*catchall}",
new { controller = "Home", action = "Index", id = UrlParameter.Optional },
new[] { "AdditionalControllers" });它们会优先去匹配URLsAndRoutes.Controllers,然后去匹配AdditionalControllers,再去匹配其他的。

如果要限定controller所在的namespace,怎么办?

Route myRoute = routes.MapRoute("AddContollerRoute", "Home/{action}/{id}/{*catchall}",
new { controller = "Home", action = "Index", id = UrlParameter.Optional },
new[] { "AdditionalControllers" });
myRoute.DataTokens["UseNamespaceFallback"] = false;
routes.Add(myRoute);myRoute会仅在AdditionalControllers中查找所指定的controller和action,如果没有直接退出route系统并报错。

8. 条件限定Route:

通过正则表达式,可以对route中的各个segment所对应的值进行限定:

routes.MapRoute("ConstraintRoute", "{controller}/{action}/{id}/{*catchall}",
new { controller = "Home", action = "Index", id = UrlParameter.Optional },
new { controller = "^H.*", action = "^(Index|About)*$", httpMethod = new HttpMethodConstraint("GET") }
);它限定controller必须以H开头,action只能是index或者about,而http method只能是get方式(这里httpMethod名称可以是其它的,MVC Framework只根据它的值类型HttpMethodConstraint来判断和取值)。

除了上述限定方法外,你还可以通过实现IRouteConstraint接口,自定义限定方式。如下:

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("CustomConstraintRoute", "{controller}/{action}/{id}/{*catchall}",
new { controller = "Home", action = "Index", id = UrlParameter.Optional },
new { controller = "^H.*", action = "Index|About", httpMethod = new HttpMethodConstraint("GET", "POST"), customConstraint = new UserAgentConstraint("IE") },
new[] { "URLsAndRoutes.Controllers" }
);可以看到,只有IE浏览器才能去访问这个route所对应的url了,当然这仅仅是一个demo而已,实际应用并不合适。

9. 用于物理文件的route:

MVC Framework的RouteTable.Routes对象有一个RouteExistingFiles属性,默认情况下它为false,即不对物理文件使用route系统。如果要使用,可以设置它为true,然后指定route。如下:

routes.RouteExistingFiles = true;
routes.MapRoute("DiskFile", "Content/StaticContent.html",
new { controller = "Account", action = "LogOn" },
new { customConstraint = new UserAgentConstraint("IE") });当访问http://mydomain.com/content/staticcontent.html时,它实际显示的是http://mydomain.com/account/logon对应的内容。

注意,不要轻易改变RouteExistingFiles的值,如果改变,务必保持其他不走route的静态内容可以访问到,如设置:

routes.IgnoreRoute("Content/{filename}.html");

二、自定义route系统:

1. 通过继承RouteBase,创建新Route:

public class LegacyRoute : RouteBase
{
private string[] urls;

public LegacyRoute(params string[] targetUrls)
{
urls = targetUrls;
}

public override RouteData GetRouteData(HttpContextBase httpContext)
{
RouteData result = null;
string requestedURL =
httpContext.Request.AppRelativeCurrentExecutionFilePath;
if (urls.Contains(requestedURL, StringComparer.OrdinalIgnoreCase))
{
result = new RouteData(this, new MvcRouteHandler());
result.Values.Add("controller", "Legacy");
result.Values.Add("action", "GetLegacyURL");
result.Values.Add("legacyURL", requestedURL);
}
return result;
}

public override VirtualPathData GetVirtualPath(RequestContext requestContext, RouteValueDictionary values)
{
//return null;

VirtualPathData result = null;
if (values.ContainsKey("legacyURL") && urls.Contains((string)values["legacyURL"], StringComparer.OrdinalIgnoreCase))
{
result = new VirtualPathData(this, new UrlHelper(requestContext).Content((string)values["legacyURL"]).Substring(1));
}
return result;
}
}可以看到在GetData方法中,暗度陈仓地指定了controller和action的值,另外还存入了一个legacyURL。

2. 实现controller和action:

public class LegacyController : Controller
{
public ActionResult GetLegacyURL(string legacyURL)
{
return View((object)legacyURL);
}
} 3. 配置Route:

routes.Add(new LegacyRoute("~/articles/Windows_3.1_Overview.html"));这样就简单完成了一个自定义Route系统。效果就是:当浏览http://mydomain.com/articles/Windows_3.1_Overview.html 时会调用刚创建的controller和action,用相同目录其他html文件来访问时,报错404.

4. 除了上述方案,还可以实现自己的RouteHandler:

public class CustomRouteHandler : IRouteHandler
{
public IHttpHandler GetHttpHandler(RequestContext requestContext)
{
return new CustomHttpHandler();
}
}

public class CustomHttpHandler : IHttpHandler
{
public bool IsReusable
{
get { return false; }
}

public void ProcessRequest(HttpContext context)
{
context.Response.Write("Hello");
}
}应用如下:

routes.Add(new Route("SayHello", new CustomRouteHandler()));这时访问http://mydomain.com/sayhello,屏幕打印出Hello。

三、使用Areas:

在vs2010的mvc项目中点右键创建Area,比如为Admin。创建完后,它就又一套自己的结构了,如下:



这个时候,如果有相同的controller和action,就要用到一种描述的在route中指定优先namespace了。另外,需要注意的是view中使用Html.ActionLink方法构造超链接时,需指定area了,如:@Html.ActionLink("Got to Admin Area", "Index", new{ area = "Admin"} 。

源码download
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: