2009-06-25 42 views
117

Tôi có hai phương pháp hành động mâu thuẫn nhau. Về cơ bản, tôi muốn có thể xem cùng một chế độ bằng hai tuyến khác nhau, hoặc bằng ID của một mục hoặc theo tên của mục và tên của mục đó (các mục có thể có cùng tên trên các phụ huynh khác nhau). Cụm từ tìm kiếm có thể được sử dụng để lọc danh sách.ASP.NET MVC phương pháp hành động mơ hồ

Ví dụ ...

Items/{action}/ParentName/ItemName 
Items/{action}/1234-4321-1234-4321 

Dưới đây là phương pháp hành động của tôi (cũng có Remove phương pháp hành động) ...

// Method #1 
public ActionResult Assign(string parentName, string itemName) { 
    // Logic to retrieve item's ID here... 
    string itemId = ...; 
    return RedirectToAction("Assign", "Items", new { itemId }); 
} 

// Method #2 
public ActionResult Assign(string itemId, string searchTerm, int? page) { ... } 

Và đây là các tuyến đường ...

routes.MapRoute("AssignRemove", 
       "Items/{action}/{itemId}", 
       new { controller = "Items" } 
       ); 

routes.MapRoute("AssignRemovePretty", 
       "Items/{action}/{parentName}/{itemName}", 
       new { controller = "Items" } 
       ); 

Tôi hiểu tại sao lỗi xảy ra, vì tham số page có thể là rỗng, nhưng tôi không thể tìm ra cách tốt nhất để giải quyết nó. Thiết kế của tôi có kém để bắt đầu không? Tôi đã nghĩ về việc mở rộng chữ ký của Method #1 để bao gồm các tham số tìm kiếm và di chuyển logic trong Method #2 ra một phương pháp riêng mà cả hai sẽ gọi, nhưng tôi không tin rằng thực sự sẽ giải quyết sự mơ hồ.

Mọi trợ giúp sẽ được đánh giá cao.


Giải pháp thực tế (dựa trên câu trả lời của Levi)

tôi đã thêm các lớp sau đây ...

public class RequireRouteValuesAttribute : ActionMethodSelectorAttribute { 
    public RequireRouteValuesAttribute(string[] valueNames) { 
     ValueNames = valueNames; 
    } 

    public override bool IsValidForRequest(ControllerContext controllerContext, MethodInfo methodInfo) { 
     bool contains = false; 
     foreach (var value in ValueNames) { 
      contains = controllerContext.RequestContext.RouteData.Values.ContainsKey(value); 
      if (!contains) break; 
     } 
     return contains; 
    } 

    public string[] ValueNames { get; private set; } 
} 

Và sau đó trang trí các phương pháp hành động ...

[RequireRouteValues(new[] { "parentName", "itemName" })] 
public ActionResult Assign(string parentName, string itemName) { ... } 

[RequireRouteValues(new[] { "itemId" })] 
public ActionResult Assign(string itemId) { ... } 
+3

Cảm ơn bạn đã đăng lên triển khai thực tế. Nó chắc chắn sẽ giúp những người có vấn đề tương tự. Như tôi đã có ngày hôm nay. :-P –

+4

Tuyệt vời! Đề xuất thay đổi nhỏ: (imo thực sự hữu ích) 1) params string [] valueNames để làm cho khai báo thuộc tính ngắn gọn hơn và (ưu tiên) 2) thay thế thân phương thức IsValidForRequest bằng 'return ValueNames.All (v => controllerContext.RequestContext.RouteData. Values.ContainsKey (v)); ' –

+0

Xin chào Jon, tôi nghĩ rằng tôi không đảm nhận điều gì đó, bởi vì đâu là các tham số truy vấn trong RouteData? – fravelgue

Trả lời

161

MVC không hỗ trợ quá tải phương thức chỉ dựa trên chữ ký, vì vậy điều này sẽ không thành công:

public ActionResult MyMethod(int someInt) { /* ... */ } 
public ActionResult MyMethod(string someString) { /* ... */ } 

Tuy nhiên, nó không hỗ trợ phương pháp quá tải dựa trên thuộc tính:

[RequireRequestValue("someInt")] 
public ActionResult MyMethod(int someInt) { /* ... */ } 

[RequireRequestValue("someString")] 
public ActionResult MyMethod(string someString) { /* ... */ } 

public class RequireRequestValueAttribute : ActionMethodSelectorAttribute { 
    public RequireRequestValueAttribute(string valueName) { 
     ValueName = valueName; 
    } 
    public override bool IsValidForRequest(ControllerContext controllerContext, MethodInfo methodInfo) { 
     return (controllerContext.HttpContext.Request[ValueName] != null); 
    } 
    public string ValueName { get; private set; } 
} 

Trong ví dụ trên, thuộc tính chỉ đơn giản nói "Phương pháp này phù hợp nếu phím xxx đã có mặt trong yêu cầu." Bạn cũng có thể lọc theo thông tin chứa trong route (controllerContext.RequestContext) nếu nó phù hợp hơn với mục đích của bạn.

+0

Điều này đã kết thúc chỉ là những gì tôi cần. Như bạn đã đề xuất, tôi cần sử dụng controllerContext.RequestContext. –

+3

Rất tốt! Tôi chưa thấy thuộc tính RequireRequestValue. Đó là một điều tốt để biết. – CoderDennis

+1

chúng ta có thể sử dụng valueprovider để lấy giá trị từ một số nguồn như: controllerContext.Controller.ValueProvider.GetValue (value); –

7

Các thông số trong tuyến đường của bạn {roleId}, {applicationName}{roleName} không khớp với tên thông số trong các phương pháp hành động của bạn. Tôi không biết điều đó có quan trọng không, nhưng nó làm cho việc tìm ra ý định của bạn là khó khăn hơn.

Mục của bạn có phù hợp với mẫu có thể được so khớp qua regex không? Nếu vậy, sau đó bạn có thể thêm một hạn chế vào tuyến đường của bạn để chỉ url phù hợp với mẫu được xác định là chứa một itemId.

Nếu ItemID của bạn chỉ chứa chữ số, sau đó điều này sẽ làm việc:

routes.MapRoute("AssignRemove", 
       "Items/{action}/{itemId}", 
       new { controller = "Items" }, 
       new { itemId = "\d+" } 
       ); 

Chỉnh sửa: Bạn cũng có thể thêm một rào cản đối với các tuyến đường AssignRemovePretty để cả hai {parentName}{itemName} được yêu cầu.

Chỉnh sửa 2: Ngoài ra, vì hành động đầu tiên của bạn chỉ chuyển hướng đến hành động thứ 2, bạn có thể loại bỏ một số sự không rõ ràng bằng cách đổi tên địa chỉ đầu tiên.

// Method #1 
public ActionResult AssignRemovePretty(string parentName, string itemName) { 
    // Logic to retrieve item's ID here... 
    string itemId = ...; 
    return RedirectToAction("Assign", itemId); 
} 

// Method #2 
public ActionResult Assign(string itemId, string searchTerm, int? page) { ... } 

Sau đó ghi rõ tên Action trong tuyến đường của bạn để buộc các phương pháp thích hợp để được gọi là:

routes.MapRoute("AssignRemove", 
       "Items/Assign/{itemId}", 
       new { controller = "Items", action = "Assign" }, 
       new { itemId = "\d+" } 
       ); 

routes.MapRoute("AssignRemovePretty", 
       "Items/Assign/{parentName}/{itemName}", 
       new { controller = "Items", action = "AssignRemovePretty" }, 
       new { parentName = "\w+", itemName = "\w+" } 
       ); 
+1

Xin lỗi Dennis, các thông số thực sự phù hợp. Tôi đã cố định câu hỏi. Tôi sẽ thử sự kiềm chế regex và lấy lại cho bạn. Cảm ơn! –

+0

Chỉnh sửa thứ hai của bạn đã giúp tôi, nhưng cuối cùng đó là gợi ý của Levi đã niêm phong thỏa thuận. Cảm ơn một lần nữa! –

0
routes.MapRoute("AssignRemove", 
       "Items/{parentName}/{itemName}", 
       new { controller = "Items", action = "Assign" } 
       ); 

xem xét sử dụng MVC đóng góp Của tuyến đường thử nghiệm thư viện để kiểm tra lộ trình của bạn

"Items/parentName/itemName".Route().ShouldMapTo<Items>(x => x.Assign("parentName", itemName)); 
1

Gần đây tôi đã có cơ hội để cải thiện @ câu trả lời Levi để hỗ trợ một phạm vi rộng của kịch bản tôi đã phải đối phó với, chẳng hạn như: hỗ trợ nhiều tham số, phù hợp với bất kỳ trong số họ (thay vì tất cả) và thậm chí không phù hợp với ai trong số họ.

Dưới đây là các thuộc tính Tôi đang sử dụng hiện nay:

/// <summary> 
/// Flags an Action Method valid for any incoming request only if all, any or none of the given HTTP parameter(s) are set, 
/// enabling the use of multiple Action Methods with the same name (and different signatures) within the same MVC Controller. 
/// </summary> 
public class RequireParameterAttribute : ActionMethodSelectorAttribute 
{ 
    public RequireParameterAttribute(string parameterName) : this(new[] { parameterName }) 
    { 
    } 

    public RequireParameterAttribute(params string[] parameterNames) 
    { 
     IncludeGET = true; 
     IncludePOST = true; 
     IncludeCookies = false; 
     Mode = MatchMode.All; 
    } 

    public override bool IsValidForRequest(ControllerContext controllerContext, MethodInfo methodInfo) 
    { 
     switch (Mode) 
     { 
      case MatchMode.All: 
      default: 
       return (
        (IncludeGET && ParameterNames.All(p => controllerContext.HttpContext.Request.QueryString.AllKeys.Contains(p))) 
        || (IncludePOST && ParameterNames.All(p => controllerContext.HttpContext.Request.Form.AllKeys.Contains(p))) 
        || (IncludeCookies && ParameterNames.All(p => controllerContext.HttpContext.Request.Cookies.AllKeys.Contains(p))) 
        ); 
      case MatchMode.Any: 
       return (
        (IncludeGET && ParameterNames.Any(p => controllerContext.HttpContext.Request.QueryString.AllKeys.Contains(p))) 
        || (IncludePOST && ParameterNames.Any(p => controllerContext.HttpContext.Request.Form.AllKeys.Contains(p))) 
        || (IncludeCookies && ParameterNames.Any(p => controllerContext.HttpContext.Request.Cookies.AllKeys.Contains(p))) 
        ); 
      case MatchMode.None: 
       return (
        (!IncludeGET || !ParameterNames.Any(p => controllerContext.HttpContext.Request.QueryString.AllKeys.Contains(p))) 
        && (!IncludePOST || !ParameterNames.Any(p => controllerContext.HttpContext.Request.Form.AllKeys.Contains(p))) 
        && (!IncludeCookies || !ParameterNames.Any(p => controllerContext.HttpContext.Request.Cookies.AllKeys.Contains(p))) 
        ); 
     } 
    } 

    public string[] ParameterNames { get; private set; } 

    /// <summary> 
    /// Set it to TRUE to include GET (QueryStirng) parameters, FALSE to exclude them: 
    /// default is TRUE. 
    /// </summary> 
    public bool IncludeGET { get; set; } 

    /// <summary> 
    /// Set it to TRUE to include POST (Form) parameters, FALSE to exclude them: 
    /// default is TRUE. 
    /// </summary> 
    public bool IncludePOST { get; set; } 

    /// <summary> 
    /// Set it to TRUE to include parameters from Cookies, FALSE to exclude them: 
    /// default is FALSE. 
    /// </summary> 
    public bool IncludeCookies { get; set; } 

    /// <summary> 
    /// Use MatchMode.All to invalidate the method unless all the given parameters are set (default). 
    /// Use MatchMode.Any to invalidate the method unless any of the given parameters is set. 
    /// Use MatchMode.None to invalidate the method unless none of the given parameters is set. 
    /// </summary> 
    public MatchMode Mode { get; set; } 

    public enum MatchMode : int 
    { 
     All, 
     Any, 
     None 
    } 
} 

Đối biết thêm thông tin và mẫu thực hiện bạn cũng có thể đọc this post how-to.

Các vấn đề liên quan