ASP.NET MVC Định tuyến Thuộc tính Phương thức [đóng]


80

Trong StackOverflow Podcast # 54 , Jeff đề cập đến việc họ đăng ký các tuyến URL của họ trong cơ sở mã StackOverflow thông qua một thuộc tính phía trên phương thức xử lý tuyến. Nghe có vẻ là một khái niệm hay (với lời cảnh báo mà Phil Haack đã đưa ra về mức độ ưu tiên của tuyến đường).

Ai đó có thể cung cấp một số mẫu để làm cho điều này xảy ra?

Ngoài ra, bất kỳ "phương pháp hay nhất" nào để sử dụng kiểu định tuyến này?

Câu trả lời:


62

CẬP NHẬT : Cái này đã được đăng trên codeplex . Mã nguồn hoàn chỉnh cũng như tập hợp được biên dịch trước có sẵn để tải xuống. Tôi chưa có thời gian để đăng tài liệu trên trang web, vì vậy bài đăng SO này sẽ phải đủ cho bây giờ.

CẬP NHẬT : Tôi đã thêm một số thuộc tính mới để xử lý 1) thứ tự tuyến, 2) ràng buộc tham số tuyến và 3) giá trị mặc định của tham số tuyến. Văn bản dưới đây phản ánh cập nhật này.

Tôi đã thực sự làm điều gì đó như thế này cho các dự án MVC của mình (tôi không biết Jeff làm như thế nào với stackoverflow). Tôi đã xác định một tập hợp các thuộc tính tùy chỉnh: UrlRoute, UrlRouteParameterConstraint, UrlRouteParameterDefault. Chúng có thể được gắn vào các phương thức hành động của bộ điều khiển MVC để khiến các tuyến, ràng buộc và mặc định tự động liên kết với chúng.

Ví dụ sử dụng:

(Lưu ý rằng ví dụ này hơi giả tạo nhưng nó thể hiện tính năng)

public class UsersController : Controller
{
    // Simple path.
    // Note you can have multiple UrlRoute attributes affixed to same method.
    [UrlRoute(Path = "users")]
    public ActionResult Index()
    {
        return View();
    }

    // Path with parameter plus constraint on parameter.
    // You can have multiple constraints.
    [UrlRoute(Path = "users/{userId}")]
    [UrlRouteParameterConstraint(Name = "userId", Regex = @"\d+")]
    public ActionResult UserProfile(int userId)
    {
        // ...code omitted

        return View();
    }

    // Path with Order specified, to ensure it is added before the previous
    // route.  Without this, the "users/admin" URL may match the previous
    // route before this route is even evaluated.
    [UrlRoute(Path = "users/admin", Order = -10)]
    public ActionResult AdminProfile()
    {
        // ...code omitted

        return View();
    }

    // Path with multiple parameters and default value for the last
    // parameter if its not specified.
    [UrlRoute(Path = "users/{userId}/posts/{dateRange}")]
    [UrlRouteParameterConstraint(Name = "userId", Regex = @"\d+")]
    [UrlRouteParameterDefault(Name = "dateRange", Value = "all")]
    public ActionResult UserPostsByTag(int userId, string dateRange)
    {
        // ...code omitted

        return View();
    }

Định nghĩa của UrlRouteAttribute:

/// <summary>
/// Assigns a URL route to an MVC Controller class method.
/// </summary>
[AttributeUsage(AttributeTargets.Method, Inherited = true, AllowMultiple = true)]
public class UrlRouteAttribute : Attribute
{
    /// <summary>
    /// Optional name of the route.  If not specified, the route name will
    /// be set to [controller name].[action name].
    /// </summary>
    public string Name { get; set; }

    /// <summary>
    /// Path of the URL route.  This is relative to the root of the web site.
    /// Do not append a "/" prefix.  Specify empty string for the root page.
    /// </summary>
    public string Path { get; set; }

    /// <summary>
    /// Optional order in which to add the route (default is 0).  Routes
    /// with lower order values will be added before those with higher.
    /// Routes that have the same order value will be added in undefined
    /// order with respect to each other.
    /// </summary>
    public int Order { get; set; }
}

Định nghĩa của UrlRouteParameterConstraintAttribute:

/// <summary>
/// Assigns a constraint to a route parameter in a UrlRouteAttribute.
/// </summary>
[AttributeUsage(AttributeTargets.Method, Inherited = true, AllowMultiple = true)]
public class UrlRouteParameterConstraintAttribute : Attribute
{
    /// <summary>
    /// Name of the route parameter on which to apply the constraint.
    /// </summary>
    public string Name { get; set; }

    /// <summary>
    /// Regular expression constraint to test on the route parameter value
    /// in the URL.
    /// </summary>
    public string Regex { get; set; }
}

Định nghĩa của UrlRouteParameterDefaultAttribute:

/// <summary>
/// Assigns a default value to a route parameter in a UrlRouteAttribute
/// if not specified in the URL.
/// </summary>
[AttributeUsage(AttributeTargets.Method, Inherited = true, AllowMultiple = true)]
public class UrlRouteParameterDefaultAttribute : Attribute
{
    /// <summary>
    /// Name of the route parameter for which to supply the default value.
    /// </summary>
    public string Name { get; set; }

    /// <summary>
    /// Default value to set on the route parameter if not specified in the URL.
    /// </summary>
    public object Value { get; set; }
}

Các thay đổi đối với Global.asax.cs:

Thay thế các lệnh gọi tới MapRoute, bằng một lệnh gọi đến hàm RouteUtility.RegisterUrlRoutesFromAttributes:

    public static void RegisterRoutes(RouteCollection routes)
    {
        routes.IgnoreRoute("{resource}.axd/{*pathInfo}");

        RouteUtility.RegisterUrlRoutesFromAttributes(routes);
    }

Định nghĩa của RouteUtility.RegisterUrlRoutesFromAttributes:

Nguồn đầy đủ được up trên codeplex . Vui lòng truy cập trang web nếu bạn có bất kỳ phản hồi hoặc báo cáo lỗi nào.


Tôi đoán làm như vậy với các thuộc tính ngăn sử dụng mặc định tuyến đường và ràng buộc tuyến đường ...
Nicolas Cadilhac

Với cách tiếp cận này, tôi chưa bao giờ cần các tuyến mặc định vì bạn đang ràng buộc mỗi tuyến với một phương pháp cụ thể. Bạn nói đúng về những ràng buộc. Tôi đã xem xét việc có thể thêm các ràng buộc như một thuộc tính thuộc tính, nhưng gặp khó khăn trong đó các ràng buộc MVC được chỉ định bằng cách sử dụng các đối tượng ẩn danh và thuộc tính chỉ có thể là các kiểu đơn giản. Tôi vẫn có thể thực hiện các ràng buộc như một thuộc tính (với nhiều mã hóa hơn), nhưng tôi chưa bận tâm đến nó vì tôi chưa thực sự cần các ràng buộc trong công việc MVC của mình cho đến thời điểm này (tôi có xu hướng xác thực các giá trị tuyến trong bộ điều khiển).
DSO

3
Rất đẹp! RouteAttribute của chúng tôi rất giống với điều này, chỉ thêm một chút chức năng trợ giúp bổ sung. Tôi sẽ phải thêm một câu trả lời chi tiết sự khác biệt.
Jarrod Dixon

1
Cái này thật tuyệt. Tôi yêu nó.
BowserKingKoopa

1
Điều đó thật tuyệt! Tôi đã sử dụng MvcContrib một thời gian và không biết điều này có ở đó. Bạn đã đề cập trong bài đăng ban đầu của mình rằng bạn không có thời gian để ghi lại nó. Đó vẫn là trường hợp? Có vẻ như ít nhất một đề cập về nó trong tài liệu MvcContrib sẽ rất hữu ích để các nhà phát triển ít nhất biết nó ở đó. Cảm ơn!
Todd Menier

44

Bạn cũng có thể thử AttributeRouting , có sẵn từ github hoặc qua nuget .

Đây là một nút thắt không biết xấu hổ, vì tôi là tác giả của dự án. Nhưng dang nếu tôi không hài lòng lắm khi sử dụng nó. Bạn có thể cũng vậy. Có rất nhiều tài liệu và mã mẫu trong wiki của kho lưu trữ github .

Với thư viện này, bạn có thể làm được nhiều việc:

  • Trang trí hành động của bạn với các thuộc tính GET, POST, PUT và DELETE.
  • Ánh xạ nhiều tuyến đường cho một hành động, sắp xếp chúng bằng thuộc tính Order.
  • Chỉ định các giá trị mặc định và ràng buộc của tuyến đường bằng cách sử dụng các thuộc tính.
  • Chỉ định các tham số tùy chọn với một đơn giản? mã thông báo trước tên tham số.
  • Chỉ định tên tuyến đường để hỗ trợ các tuyến đường được đặt tên.
  • Xác định khu vực MVC trên bộ điều khiển hoặc bộ điều khiển cơ sở.
  • Nhóm hoặc lồng các tuyến của bạn lại với nhau bằng cách sử dụng tiền tố tuyến được áp dụng cho bộ điều khiển hoặc bộ điều khiển cơ sở.
  • Hỗ trợ các url kế thừa.
  • Đặt mức độ ưu tiên của các tuyến trong số các tuyến được xác định cho một hành động, trong bộ điều khiển và giữa các bộ điều khiển và bộ điều khiển cơ sở.
  • Tự động tạo url gửi đi chữ thường.
  • Xác định các quy ước tuyến đường tùy chỉnh của riêng bạn và áp dụng chúng trên bộ điều khiển để tạo các tuyến đường cho các hành động bên trong bộ điều khiển mà không cần thuộc tính soạn sẵn (nghĩ kiểu RESTful).
  • Gỡ lỗi các tuyến đường của bạn bằng HttpHandler được cung cấp.

Tôi chắc rằng có một số thứ khác mà tôi đang quên. Kiểm tra nó ra. Thật dễ dàng để cài đặt thông qua nuget.

LƯU Ý: Kể từ ngày 16/4/12, AttributeRouting cũng hỗ trợ cơ sở hạ tầng API Web mới. Chỉ trong trường hợp bạn đang tìm kiếm thứ gì đó có thể xử lý được điều đó. Cảm ơn subkamran !


10
Dự án này có vẻ trưởng thành hơn (tài liệu tốt hơn, nhiều tính năng, bộ kiểm tra hoàn chỉnh) so với các tùy chọn khác được đề cập
David Laing

3
Tôi đồng ý, điều này dường như làm mọi thứ bạn có thể muốn và với tài liệu ví dụ tốt.
Mike Chamberlain

3
Cảm ơn rât nhiều. Tôi rất vui khi sử dụng giải pháp này và nó đã giải quyết được tất cả các xung đột định tuyến, sự mơ hồ và nhầm lẫn của tôi.
Valamas

3
Hey tại chỗ, bạn nên viết các điểm đạn trên trên trang github của bạn, như tôi đã tìm thấy bài SO này như tôi là trên hunt cho biết thêm chi tiết :)
GONeale

2
Chỉ để chơi người ủng hộ quỷ, có lợi ích gì khi khai báo tất cả các tuyến đường của bạn ở một nơi không? Giống như chúng ta có mất gì không, hoặc bị hạn chế trong bất kỳ thời trang nào khi chuyển sang phương pháp này?
GONeale

9

1. Tải xuống RiaLibrary.Web.dll và tham chiếu nó trong dự án trang web ASP.NET MVC của bạn

2. Giải mã các phương thức bộ điều khiển với các thuộc tính [Url]:

public SiteController : Controller
{
    [Url("")]
    public ActionResult Home()
    {
        return View();
    }

    [Url("about")]
    public ActionResult AboutUs()
    {
        return View();
    }

    [Url("store/{?category}")]
    public ActionResult Products(string category = null)
    {
        return View();
    }
}

BTW, '?' đăng nhập tham số '{? category}' có nghĩa là nó là tùy chọn. Bạn sẽ không cần chỉ định điều này một cách rõ ràng trong mặc định của tuyến, giá trị này tương đương với điều này:

routes.MapRoute("Store", "store/{category}",
new { controller = "Store", action = "Home", category = UrlParameter.Optional });

3. Cập nhật tệp Global.asax.cs

public class MvcApplication : System.Web.HttpApplication
{
    public static void RegisterRoutes(RouteCollection routes)
    {
        routes.IgnoreRoute("{resource}.axd/{*pathInfo}");

        routes.MapRoutes(); // This does the trick
    }

    protected void Application_Start()
    {
        RegisterRoutes(RouteTable.Routes);
    }
}

Cách đặt giá trị mặc định và ràng buộc? Thí dụ:

public SiteController : Controller
{
    [Url("admin/articles/edit/{id}", Constraints = @"id=\d+")]
    public ActionResult ArticlesEdit(int id)
    {
        return View();
    }

    [Url("articles/{category}/{date}_{title}", Constraints =
         "date=(19|20)\d\d-(0[1-9]|1[012])-(0[1-9]|[12][0-9]|3[01])")]
    public ActionResult Article(string category, DateTime date, string title)
    {
        return View();
    }
}

Làm thế nào để thiết lập đặt hàng? Thí dụ:

[Url("forums/{?category}", Order = 2)]
public ActionResult Threads(string category)
{
    return View();
}

[Url("forums/new", Order = 1)]
public ActionResult NewThread()
{
    return View();
}

1
Rất đẹp! Tôi đặc biệt thích {?param}danh pháp cho các tham số tùy chọn.
Jarrod Dixon

3

Bài đăng này chỉ là để mở rộng câu trả lời của DSO.

Trong khi chuyển đổi các tuyến đường của tôi thành thuộc tính, tôi cần xử lý thuộc tính ActionName. Vì vậy, trong GetRouteParamsFromAttribute:

ActionNameAttribute anAttr = methodInfo.GetCustomAttributes(typeof(ActionNameAttribute), false)
    .Cast<ActionNameAttribute>()
    .SingleOrDefault();

// Add to list of routes.
routeParams.Add(new MapRouteParams()
{
    RouteName = routeAttrib.Name,
    Path = routeAttrib.Path,
    ControllerName = controllerName,
    ActionName = (anAttr != null ? anAttr.Name : methodInfo.Name),
    Order = routeAttrib.Order,
    Constraints = GetConstraints(methodInfo),
    Defaults = GetDefaults(methodInfo),
});

Ngoài ra tôi thấy việc đặt tên tuyến đường không phù hợp. Tên được xây dựng động với controllerName.RouteName. Nhưng tên tuyến đường của tôi là chuỗi const trong lớp bộ điều khiển và tôi sử dụng const đó để gọi Url.RouteUrl. Đó là lý do tại sao tôi thực sự cần tên tuyến đường trong thuộc tính là tên thực của tuyến đường.

Một điều khác mà tôi sẽ làm là chuyển đổi các thuộc tính mặc định và ràng buộc thành AttributeTargets.Parameter để tôi có thể gắn chúng vào các tham số.


Vâng, tôi hơi dao động về hành vi đặt tên tuyến đường. Có lẽ tốt nhất là làm những gì bạn đã làm, chỉ cần sử dụng whats trong thuộc tính nguyên trạng hoặc làm cho nó không có giá trị. Ý tưởng hay khi đặt mặc định / ràng buộc vào chính các tham số. Có thể tôi sẽ đăng bài này trên codeplex vào một lúc nào đó để quản lý các thay đổi tốt hơn.
DSO

0

Tôi đã kết hợp hai cách tiếp cận này thành một phiên bản Frankensteinian cho bất kỳ ai muốn nó. (Tôi thích ký hiệu tham số tùy chọn, nhưng cũng nghĩ rằng chúng nên là các thuộc tính riêng biệt khỏi mặc định / ràng buộc hơn là tất cả được trộn thành một).

http://github.com/djMax/AlienForce/tree/master/Utilities/Web/


0

Tôi cần định tuyến ITCloud hoạt động trong asp.net mvc 2 bằng cách sử dụng AsyncController - để làm như vậy, chỉ cần chỉnh sửa lớp RouteUtility.cs trong nguồn và biên dịch lại. Bạn phải loại bỏ "Đã hoàn thành" khỏi tên hành động trên dòng 98

// Add to list of routes.
routeParams.Add(new MapRouteParams()
{
    RouteName = String.IsNullOrEmpty(routeAttrib.Name) ? null : routeAttrib.Name,
    Path = routeAttrib.Path,
    ControllerName = controllerName,
    ActionName = methodInfo.Name.Replace("Completed", ""),
    Order = routeAttrib.Order,
    Constraints = GetConstraints(methodInfo),
    Defaults = GetDefaults(methodInfo),
    ControllerNamespace = controllerClass.Namespace,
});

Sau đó, trong AsyncController, trang trí XXXXCompleted ActionResult bằng các thuộc tính UrlRoutevà quen UrlRouteParameterDefaultthuộc:

[UrlRoute(Path = "ActionName/{title}")]
[UrlRouteParameterDefault(Name = "title", Value = "latest-post")]
public ActionResult ActionNameCompleted(string title)
{
    ...
}

Hy vọng rằng sẽ giúp một ai đó với cùng một vấn đề.


FYI, quy ước là có các thuộc tính liên quan đến MVC trên phương thức ActionNameAsync chứ không phải phương thức ActionNameCompleted.
Erv Walter

Cảm ơn - đã không nhận ra điều đó.
TimDog
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.