2010-10-05 22 views
21

Tôi đang cố gắng viết một tuyến đường có chứa một giá trị rỗng trong đó. Bạn có thể truy cập cả hai /profile/ nhưng cũng có thể là /profile/\d+.ASP.NET MVC: Định tuyến với thông số tùy chọn, nhưng nếu được cung cấp, phải khớp với d +

routes.MapRoute("ProfileDetails", "profile/{userId}", 
       new {controller = "Profile", 
        action = "Details", 
        userId = UrlParameter.Optional}, 
       new {userId = @"\d+"}); 

Như bạn thấy, tôi nói rằng userId là không bắt buộc mà còn là nó phải phù hợp với biểu thức chính quy \d+. Điều này không hiệu quả và tôi hiểu tại sao.

Nhưng làm cách nào tôi có thể xây dựng tuyến đường phù hợp với chỉ /profile/ mà còn /profile/ theo sau là một số?

Trả lời

26

Các đơn giản nhất cách sẽ được chỉ cần thêm tuyến đường khác mà không cần tham số userId, vì vậy bạn có một dự phòng:

routes.MapRoute("ProfileDetails", "profile/{userId}", 
       new {controller = "Profile", 
        action = "Details", 
        userId = UrlParameter.Optional}, 
       new {userId = @"\d+"}); 

routes.MapRoute("Profile", "profile", 
       new {controller = "Profile", 
        action = "Details"}); 

Theo như tôi biết, chỉ có cách khác mà bạn có thể làm được điều này sẽ với ràng buộc tùy chỉnh. Vì vậy, tuyến đường của bạn sẽ trở thành:

routes.MapRoute("ProfileDetails", "profile/{userId}", 
       new {controller = "Profile", 
        action = "Details", 
        userId = UrlParameter.Optional}, 
       new {userId = new NullableConstraint()); 

Và mã hạn chế tùy chỉnh sẽ trông như thế này:

using System; 
using System.Web; 
using System.Web.Routing; 
using System.Web.Mvc; 

namespace YourNamespace 
{ 
    public class NullableConstraint : IRouteConstraint 
    { 
     public bool Match(HttpContextBase httpContext, Route route, string parameterName, RouteValueDictionary values, RouteDirection routeDirection) 
     { 
      if (routeDirection == RouteDirection.IncomingRequest && parameterName == "userId") 
      { 
       // If the userId param is empty (weird way of checking, I know) 
       if (values["userId"] == UrlParameter.Optional) 
        return true; 

       // If the userId param is an int 
       int id; 
       if (Int32.TryParse(values["userId"].ToString(), out id)) 
        return true; 
      } 

      return false; 
     } 
    } 
} 

Tôi không biết rằng NullableConstraint là tên tốt nhất ở đây, nhưng đó là tùy thuộc vào bạn!

+0

Tôi đã làm một cái gì đó tương tự như thế này, nhưng hơi chung chung hơn, với một nhà xây dựng có một biểu thức chính quy để phù hợp với giá trị được cung cấp. Cảm ơn! –

+0

Tôi biết tôi đến muộn ở đây, nhưng không phải là dòng cuối cùng sẽ trở lại đúng không? Nếu không, ràng buộc luôn luôn thất bại nếu hướng tuyến là UrlGeneration. –

+0

Nếu dòng cuối cùng trả về true thì * mọi thứ * sẽ khớp với ràng buộc ... nhưng có, bạn nói đúng, điều này không tính đến bất kỳ điều gì ngoài định tuyến yêu cầu đến. Nhìn vào nó bây giờ, tôi đã nghĩ rằng chúng ta chỉ có thể loại bỏ phần routeDirection của câu lệnh 'if' hoàn toàn; Tôi sẽ kiểm tra vào ngày mai và chỉnh sửa nếu đó là trường hợp. –

3

regex của bạn có nên là \ d * không?

+0

với '\ d *' nó không phù hợp ở tất cả, có thể có hoặc không có 'UrlParameter.Optional'. –

+3

@DenizDogan Không chắc chắn nếu một cái gì đó thay đổi kể từ đó nhưng tôi chỉ thử nghiệm '\ d *' và nó thực hiện chính xác điều tương tự như 'OptionalRegExConstraint'. – Chev

2

Nhờ có Mark Bell cho câu trả lời này, nó đã giúp tôi khá nhiều.

Tôi tự hỏi tại sao bạn khó mã hóa việc kiểm tra "userId" trong ràng buộc? Tôi hơi viết lại lớp của bạn như để sử dụng các tham số parameterName, và nó có vẻ là làm việc tốt.

Tôi có thiếu gì bằng cách thực hiện theo cách này không?

public class OptionalRegExConstraint : IRouteConstraint 
{ 
    private readonly Regex _regEx; 

    public OptionalRegExConstraint(string matchExpression=null) 
    { 
     if (!string.IsNullOrEmpty(matchExpression)) 
      _regEx = new Regex(matchExpression); 
    } 

    public bool Match(HttpContextBase httpContext, Route route, string parameterName, RouteValueDictionary values, RouteDirection routeDirection) 
    { 
     if (routeDirection == RouteDirection.IncomingRequest) 
     { 
      if (values[parameterName] == UrlParameter.Optional) return true; 

      return _regEx != null && _regEx.Match(values[parameterName].ToString()).Success; 
     } 
     return false; 
    } 
} 
+0

Tôi đã mã hóa cứng giá trị đó chỉ vì lợi ích của sự rõ ràng trong ví dụ, nhưng bạn nói đúng — cách tiếp cận này linh hoạt hơn tôi. +1 –

13

Nó có thể một cái gì đó đã thay đổi kể từ khi câu hỏi này được trả lời nhưng tôi đã có thể thay đổi điều này:

routes.MapPageRoute(
    null, 
    "projects/{operation}/{id}", 
    "~/Projects/ProjectWizard.aspx", 
    true, 
    new RouteValueDictionary(new 
    { 
     operation = "new", 
     id = UrlParameter.Optional 
    }), 
    new RouteValueDictionary(new 
    { 
     id = new NullableExpressionConstraint(@"\d+") 
    }) 
); 

Với điều này:

routes.MapPageRoute(
    null, 
    "projects/{operation}/{id}", 
    "~/Projects/ProjectWizard.aspx", 
    true, 
    new RouteValueDictionary(new 
    { 
     operation = "new", 
     id = UrlParameter.Optional 
    }), 
    new RouteValueDictionary(new 
    { 
     id = @"\d*" 
    }) 
); 

Đơn giản chỉ cần sử dụng * thay vì + trong biểu thức chính quy hoàn thành nhiệm vụ tương tự. Tuyến đường vẫn được kích hoạt nếu tham số không được bao gồm, nhưng nếu được bao gồm, nó sẽ chỉ kích hoạt nếu giá trị là một số nguyên hợp lệ. Nếu không nó sẽ thất bại.

+0

Làm việc cho tôi. Cảm ơn. –

7

ASP.NET MVC 3 đã giải quyết được vấn đề này và như Alex Ford brought out, bạn có thể sử dụng \d* thay vì viết một ràng buộc tùy chỉnh. Nếu mẫu của bạn phức tạp hơn, như tìm kiếm một năm với \d{4}, chỉ cần đảm bảo mẫu của bạn khớp với những gì bạn muốn cũng như một chuỗi trống, như (\d{4})? hoặc \d{4}|^$. Bất cứ điều gì làm cho bạn hạnh phúc.

Nếu bạn vẫn đang sử dụng ASP.NET MVC 2 và muốn sử dụng Mark Bell's example hoặc NYCChris' example, hãy lưu ý rằng con đường sẽ phù hợp miễn là tham số URL chứa một trận đấu để mô hình của bạn.Điều này có nghĩa là mẫu \d+ sẽ khớp với các thông số như abc123def. Để tránh điều này, hãy bọc mẫu với ^()$ khi xác định tuyến đường của bạn hoặc trong ràng buộc tùy chỉnh. (Nếu bạn nhìn vào System.Web.Routing.Route.ProcessConstraint trong Reflector, bạn sẽ thấy rằng nó thực hiện điều này cho bạn khi sử dụng được xây dựng trong chế. Nó cũng đặt CultureInvariant, Compiled, and IgnoreCase tùy chọn.)

Kể từ khi tôi đã viết hạn chế tùy chỉnh của riêng tôi với hành vi mặc định đề cập ở trên trước khi nhận ra tôi không phải sử dụng nó, tôi sẽ để nó ở đây:

public class OptionalConstraint : IRouteConstraint 
{ 
    public OptionalConstraint(Regex regex) 
    { 
    this.Regex = regex; 
    } 

    public OptionalConstraint(string pattern) : 
    this(new Regex("^(" + pattern + ")$", 
     RegexOptions.CultureInvariant | 
     RegexOptions.Compiled | 
     RegexOptions.IgnoreCase)) { } 

    public Regex Regex { get; set; } 

    public bool Match(HttpContextBase httpContext, 
        Route route, 
        string parameterName, 
        RouteValueDictionary values, 
        RouteDirection routeDirection) 
    { 
    if(routeDirection == RouteDirection.IncomingRequest) 
    { 
     object value = values[parameterName]; 
     if(value == UrlParameter.Optional) 
     return true; 
     if(this.Regex.IsMatch(value.ToString())) 
     return true; 
    } 

    return false; 
    } 
} 

Và đây là một con đường dụ:

routes.MapRoute("PostsByDate", 
       "{year}/{month}", 
       new { controller = "Posts", 
         action = "ByDate", 
         month = UrlParameter.Optional }, 
       new { year = @"\d{4}", 
         month = new OptionalConstraint(@"\d\d") }); 
+0

ASP.NET MVC 4 cũng cho phép biểu thức chính quy phù hợp với các chuỗi rỗng như '(\ d {4})?', Điều này làm tôi hạnh phúc. –

+1

Có lẽ đã đến lúc câu trả lời này phải được đánh dấu là đúng? –

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