Fork me on GitHub

ASP.NET MVC 中的路由

普通的路由

在以前版本的ASP.NET MVC里,这些规则会被定义在RouteConfig.cs文件,也有Routing Assistant扩展可以实现基于特性的路由,不过是收费滴,另外还有一个扩展:http://attributerouting.net/ ,也很不错;理论上ASP.NET MVC 中要实现任意的规则URL 应该是没有问题的。

Attribute routing

ASP.NET MVC now supports attribute routing, thanks to a contribution by Tim McCall, the author of http://attributerouting.net. With attribute routing you can specify your routes by annotating your actions and controllers.

比如配置的酒店详情页路由

              //HOTEL DETAIL
            routes.MapRoute(name: "HotelDetail",
                        url: "hotel/{hotelCd}",
                        defaults: new { controller = "Hotel", action = "HotelDetail" },
                        constraints: new { controller = "Hotel", hotelCd = @"[a-zA-Z0-9]*" }
            );

自定义路由约束

如果是要精确到数据是否准确,可以自定义的路由规则,实现 IRouteConstraint 接口,重写Match方法即可。

public class CityConstraint : IRouteConstraint
    {
        /// <summary>
        /// 匹配二级城市的URL
        /// </summary>
        /// <param name="httpContext"></param>
        /// <param name="route"></param>
        /// <param name="parameterName"></param>
        /// <param name="values"></param>
        /// <param name="routeDirection"></param>
        /// <returns></returns>
        public bool Match(HttpContextBase httpContext, Route route, string parameterName, RouteValueDictionary values, RouteDirection routeDirection)
        {
            try
            {
                if (values["cityName"] == null || values["cityName"].ToString().ContainsAny(new string[] { "home", "list", "hotel", "member", "activity", "ajax","alipay", "gift"}))
                {
                    return false;
                }
                var city = CacheData.getCitys().Where(data => data.Pinyin.Equals(values["cityName"].ToString(), StringComparison.OrdinalIgnoreCase)).ToList();
                if (city == null || city.Count == 0)
                {
                    return false;
                }
                return true;
            }
            catch (Exception ex)
            {
                return false;
            }
        }
           routes.MapRoute(
               name: "HotelCity",
               url: "{cityName}/p{pageNo}",
               defaults: new { controller = "HotelSEO", action = "Hotel", pageNo = "1" },
               constraints: new { cityName = new CityConstraint(), pageNo = @"\d+" }
                //constraints: new { cityName = @"(|shanghai|beijing|hefei|chuzhou)"}
             );

考虑到hotel/xxx可以匹配到酒店编号不符合规则的酒店,也可能是用户手误打错了,可以捕获所有以hotel开头不符合正则的的URL到酒店的目录页

routes.MapRoute(name: "HotelMenu",
                           url: "hotel/{*values}",
                           defaults: new { controller = "Menu", action = "Index" }
            );

特性路由

要启用特性路由,只要项目中注册MapMvcAttributeRoutes方法

public static void RegisterRoutes(RouteCollection routes)
        {
            routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
            //启用特性路由
              routes.MapMvcAttributeRoutes();
            routes.MapRoute(
                name: "Default",
                url: "{controller}/{action}/{id}",
                defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional }
            );
        }

可选的URL参数和默认值

    // eg: /hotel
    // eg: /hotel/123456
    [Route("hotel/{hotelCd?}")]
    public ActionResult View(string hotelCd)
    {
        if (!String.IsNullOrEmpty(hotelCd))
        {
            return View("HotelDetail", GetHotelDetail(hotelCd));
        }
        return View("HotelMenu", GetHotelCitys());
    }

路由前缀

比如现在的会员页面,都是以user 开头的如

// eg: /user
    [Route("user")]
    public ActionResult Index() { ... }
    // eg: /user/5
    [Route("user/{userId}")]
    public ActionResult Show(int userId) { ... }
    // eg: /user/5/edit
    [Route("user/{userId}/edit")]
    public ActionResult Edit(int userId) { ... }

这个时候也在控制器加一个全局[RoutePrefix]特性

[RoutePrefix("user")]
public class userController : Controller
{
    // eg.: /user
    [Route]
    public ActionResult Index() { ... }
    // eg.: /user/5
    [Route("{userId}")]
    public ActionResult Show(int userId) { ... }
    // eg.: /user/5/edit
    [Route("{userId}/edit")]
    public ActionResult Edit(int userId) { ... }
}

重写路由规则

如果有的时候需要重写路由规则,就加个波浪线 ~

[RoutePrefix("review")]
public class ReviewController : Controller
{
    // eg.: /code-review
    [Route("~/code-review")]
    public ActionResult CodeView() { ... }
}

也可以在controller中定义Route特性,将action作为一个参数。这个路由会被应用到controller上的所有action,除非某个action已经定义了一个[Route]特性,重写controller的默认行为。

[RoutePrefix("user")]
[Route("{action=Index}")]
public class ReviewsController : Controller
{
    // eg.: /user
    public ActionResult Index() { ... }
 
    // eg.: /user/set
    public ActionResult Set() { ... }
 
    // eg.: /user/new
    public ActionResult New() { ... }
 
    // eg.: /user/edit/5
    [Route("edit/{userId:int}")]
    public ActionResult Edit(int promoId) { ... }
}

路由约束

路由约束让你可以限制参数在路由模板里如何匹配。通常的语法是{parameter:constraint},如:

// eg: /users/5
[Route("users/{id:int}"]
public ActionResult GetUserById(int id) { ... }
// eg: users/ken
[Route("users/{name}"]
public ActionResult GetUserByName(string name) { ... }

支持的约束类型:

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}$)}

可以用分号分隔,定义多个约束,如:

// 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) { ... }

比如MS的这篇文章,大致可以配置成如下的路由规则:
http://blogs.msdn.com/b/webdev/archive/2014/03/04/asp-net-mvc-5-lifecycle-document-published.aspx
[Route("/b/webdev/archive/{year:regex(\\d{4})}")]
[Route("/b/webdev/archive/{year:regex(\\d{4})}/{month:regex(\\d{1,2})}")]
[Route("/b/webdev/archive/{year:regex(\\d{4})}/{month:regex(\\d{1,2})}/{day:regex(\\d{1,2})}/{slug}")]
public ActionResult GetArchivePost(string year, string month,string day,string slug){ };

特性路由中自定义路由约束

public class ValuesConstraint : IRouteConstraint
{
    private readonly string[] validOptions;
    public ValuesConstraint(string options)
    {
        validOptions = options.Split('|');
    }
 
    public bool Match(HttpContextBase httpContext, Route route, string parameterName, RouteValueDictionary values, RouteDirection routeDirection)
    {
        object value;
        if (values.TryGetValue(parameterName, out value) && value != null)
        {
            return validOptions.Contains(value.ToString(), StringComparer.OrdinalIgnoreCase);
        }
        return false;
    }
}

注册路由

public class RouteConfig
{
    public static void RegisterRoutes(RouteCollection routes)
    {
        routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
        var constraintsResolver = new DefaultInlineConstraintResolver();
        constraintsResolver.ConstraintMap.Add("values", typeof(ValuesConstraint));
        routes.MapMvcAttributeRoutes(constraintsResolver);
    }
}
应用新的路由规则
public class TemperatureController : Controller
{
    // eg: temp/celsius and /temp/fahrenheit but not /temp/kelvin
    [Route("temp/{scale:values(celsius|fahrenheit)}")]
    public ActionResult Show(string scale)
    {
        return Content("scale is " + scale);
    }
}

路由名称


可以给一个路由指定一个名字,以便于URL生成,如:

[Route("menu", Name = "mainmenu")]
public ActionResult MainMenu() { ... }

使用@Url.RouteUrl 生成一个超链接

<a href="@Url.RouteUrl("mainmenu")">Main menu</a>

特性路由VS普通的路由

其实这个问题与Spring是基于xml配置好,还是注解的方式好,具体还得看使用场景,每一种方式都有自己的优势与劣势,比如基于普通的路由可以自定义直接 的一些逻辑,基于特性的路由可以快速现实一个路由的实现。

        // eg: /books/lang
        // eg: /books/lang/en
        // eg: /books/lang/he
        [Route("books/lang/{lang=en}")]
        public ActionResult Lang(string lang)
        {
            return View();
        }

        [Route("~/attribute-routing-in-asp-net-mvc-5")]
        public ActionResult Hotel()
        {
            return Content("I'am mvc attribute route! ");
        }

Refer:
What's New in ASP.NET MVC 5
http://www.asp.net/mvc/mvc5
What’s New in ASP.NET MVC 5.2
http://www.asp.net/mvc/overview/releases/whats-new-in-aspnet-mvc-52
attribute-routing-in-asp-net-mvc-5
http://blogs.msdn.com/b/webdev/archive/2013/10/17/attribute-routing-in-asp-net-mvc-5.aspx

posted @ 2014-09-27 17:07  花儿笑弯了腰  阅读(605)  评论(0编辑  收藏  举报