Làm thế nào tôi có thể có các tuyến chữ thường trong ASP.NET MVC?


145

Làm thế nào tôi có thể có chữ thường, cộng với gạch dưới nếu có thể, các tuyến đường trong ASP.NET MVC? Vì vậy, tôi sẽ có /dinners/details/2cuộc gọi DinnersController.Details(2)và, nếu có thể, /dinners/more_details/2gọi DinnersController.MoreDetails(2)?

Tất cả điều này trong khi vẫn sử dụng các mẫu như {controller}/{action}/{id}.


Cuối cùng tôi đã viết tất cả các tuyến đường của mình bằng tay vì nhiều lý do và tôi nghĩ thật khó để tránh làm điều đó với bất cứ điều gì không chỉ là CRUD. Vì vậy, tôi chỉ viết chúng bằng chữ thường.
Pupeno

Sử dụng biểu mẫu web ? Truy cập tại đây: msdn.microsoft.com/en-us/l Library / . (Tôi đang dần chuyển đổi dự án của mình từ các biểu mẫu web sang MVC và có cả hai trong dự án)
Jess

Tôi khá chắc chắn rằng bạn có thể làm điều này như mặc định

Tôi không nghĩ nó quan trọng nếu bạn nhập các tuyến trong chữ thường hoặc chữ hoa.

Câu trả lời:


238

Với System.Web.Routing 4.5, bạn có thể thực hiện điều này một cách đơn giản bằng cách đặt thuộc tính LowercaseUrls của RouteCollection:

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

        routes.LowercaseUrls = true;

        routes.MapRoute(
            name: "Default",
            url: "{controller}/{action}/{id}",
            defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional }
        );
    }

Cũng giả sử rằng bạn đang làm điều này vì lý do SEO, bạn muốn chuyển hướng các url đến chữ thường (như đã nói trong nhiều liên kết ngoài bài viết này).

protected void Application_BeginRequest(object sender, EventArgs e)
{
  //You don't want to redirect on posts, or images/css/js
  bool isGet = HttpContext.Current.Request.RequestType.ToLowerInvariant().Contains("get");
  if (isGet && HttpContext.Current.Request.Url.AbsolutePath.Contains(".") == false)    
  {
     string lowercaseURL = (Request.Url.Scheme + "://" + HttpContext.Current.Request.Url.Authority + HttpContext.Current.Request.Url.AbsolutePath);
     if (Regex.IsMatch(lowercaseURL, @"[A-Z]"))
     {
      //You don't want to change casing on query strings
      lowercaseURL = lowercaseURL.ToLower() + HttpContext.Current.Request.Url.Query;

      Response.Clear();
      Response.Status = "301 Moved Permanently";
      Response.AddHeader("Location", lowercaseURL); 
      Response.End();
    }
 }
}

4
Đây là điều đơn giản nhất để làm trong 4.0.
Paul Turner

1
Ước gì điều này ở trên đỉnh ... Hầu như rất nhiều mã hóa!
akatakritos

2
Câu trả lời tuyệt vời :-) Phần SEO sau phù hợp độc đáo với mô-đun HTTP.
David Kirkland

1
@Aaron Sherman Phần Application_BeginRequest được đặt ở đâu? Nó gây ra lỗi cho tôi khi nó ở bên trong lớp công khai RouteConfig và cả khi nó nằm ngoài if.
Richard Mišenčík

1
@ richard-mišenčík thêm nó vào tệp Global.asax
ITmeze

44

Hai hướng dẫn này đã giúp khi tôi muốn làm điều tương tự và hoạt động tốt:

http://www.coderjournal.com/2008/03/force-mvc-route-url-lowercase/ http://goneale.com/2008/12/19/lowercase-route-urls-in-aspnet-mvc/

EDIT: Đối với các dự án có khu vực, bạn cần sửa đổi phương thức GetVirtualPath ():

public override VirtualPathData GetVirtualPath(RequestContext requestContext, RouteValueDictionary values)
{
  var lowerCaseValues = new RouteValueDictionary();

  foreach (var v in values)
  {
    switch (v.Key.ToUpperInvariant())
    {
      case "ACTION":
      case "AREA":
      case "CONTROLLER":
        lowerCaseValues.Add(v.Key, ((string)v.Value).ToLowerInvariant());
        break;
      default:
        lowerCaseValues.Add(v.Key.ToLowerInvariant(), v.Value);
        break;
    }
  }
  return base.GetVirtualPath(requestContext, lowerCaseValues);
}

9
Liên kết GONeale đã thay đổi; URL hiện là goneale.com/2008/12/19/lowercase-route-urls-in-aspnet-mvc
Daniel Liuzzi

4
@Derek - Không, hướng dẫn bị hỏng khi sử dụng Area. Sau 3 ngày thử MỌI THỨ ... Tôi đã tìm thấy một giải pháp tốt hơn, có một thư viện gọi là Định tuyến thuộc tính. Giải quyết vấn đề và làm cho cuộc sống dễ dàng hơn rất nhiều. philliphaydon.com/2011/08/ mài
Phill

6
Đối với mvc 4, có một giải pháp tốt hơn bằng cách sử dụng các tuyến thuộc tính.LowercaseUrls = true; Thông tin thêm về dhuvelle.com/2012/11/tips-for-aspnet-mvc-4-lowercase-urls.html
Marc Cals

4
@GONeale Điều gì đã xảy ra với URL của bạn? Liên kết thứ hai đã chết ngay bây giờ! Mặc dù làm tôi cười, tiêu đề là " Trang web Gone Ale tốt nhất trên Internet"
Luke

2
@chteuchteu liên kết đó không hoạt động với tôi, nhưng liên kết này đã làm.
chue x


21

Nếu bạn đang sử dụng UrlHelper để tạo liên kết, bạn chỉ cần chỉ định tên của hành động và bộ điều khiển là chữ thường:

itemDelete.NavigateUrl = Url.Action("delete", "photos", new { key = item.Key });

Kết quả trong: / media / photos / xóa / 64 (mặc dù bộ điều khiển và hành động của tôi là trường hợp pascal).


16
Tôi nghĩ rằng làm công việc này ở một vị trí trung tâm là giải pháp dễ dàng và tiêu chuẩn nhất. Điều này cũng tệ như CSS nội tuyến. (Rõ ràng 15 người sử dụng CSS nội tuyến).
Người đàn ông Muffin

Điều đó có đúng với các máy chủ Linux không?
QMaster

15

Tôi đã tìm thấy điều này tại Tạp chí Coder của Nick Berard , nhưng nó không có thông tin về cách triển khai LowercaseRoutelớp. Do đó đăng lại ở đây với thông tin bổ sung.

Đầu tiên mở rộng Routelớp đểLowercaseRoute

public class LowercaseRoute : Route
{
    public LowercaseRoute(string url, IRouteHandler routeHandler)
        : base(url, routeHandler) { }
    public LowercaseRoute(string url, RouteValueDictionary defaults, IRouteHandler routeHandler)
        : base(url, defaults, routeHandler) { }
    public LowercaseRoute(string url, RouteValueDictionary defaults, RouteValueDictionary constraints, IRouteHandler routeHandler)
        : base(url, defaults, constraints, routeHandler) { }
    public LowercaseRoute(string url, RouteValueDictionary defaults, RouteValueDictionary constraints, RouteValueDictionary dataTokens, IRouteHandler routeHandler) : base(url, defaults, constraints, dataTokens, routeHandler) { }
    public override VirtualPathData GetVirtualPath(RequestContext requestContext, RouteValueDictionary values)
    {
        VirtualPathData path = base.GetVirtualPath(requestContext, values);

        if (path != null)
            path.VirtualPath = path.VirtualPath.ToLowerInvariant();

        return path;
    }
}

Sau đó sửa đổi RegisterRoutesphương thức của Global.asax.cs

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

    routes.Add(new LowercaseRoute("{controller}/{action}/{id}", 
        new RouteValueDictionary(new { controller = "Home", action = "Index", id = "" }), 
        new MvcRouteHandler()));

    //routes.MapRoute(
    //    "Default",                                              // Route name
    //    "{controller}/{action}/{id}",                           // URL with parameters
    //    new { controller = "Home", action = "Index", id = "" }  // Parameter defaults
    //);
}

Tuy nhiên, tôi muốn biết một cách để sử dụng tuyến đường.MapRoute ...


Bài viết của GONeale cung cấp một phương thức mở rộng để bạn có thể viết routes.MapRouteLowercase(...cái nào đẹp hơn ở trên: goneale.wordpress.com/2008/12/19/ mẹo
Drew Noakes

1
Toàn bộ blog của GONeale biến mất. Đây là một mục blog khác có nội dung tương tự (và cùng một phương pháp mở rộng). Nó giải quyết tình huống này trong bối cảnh giảm nội dung trùng lặp.
patridge

11

Bạn có thể tiếp tục sử dụng cú pháp MapRoute bằng cách thêm lớp này làm phần mở rộng cho RouteCollection:

public static class RouteCollectionExtension
{
    public static Route MapRouteLowerCase(this RouteCollection routes, string name, string url, object defaults)
    {
        return routes.MapRouteLowerCase(name, url, defaults, null);
    }

    public static Route MapRouteLowerCase(this RouteCollection routes, string name, string url, object defaults, object constraints)
    {
        Route route = new LowercaseRoute(url, new MvcRouteHandler())
        {
            Defaults = new RouteValueDictionary(defaults),
            Constraints = new RouteValueDictionary(constraints)
        };

        routes.Add(name, route);

        return route;
    }
}

Bây giờ bạn có thể sử dụng trong phần khởi động "MapRouteLowerCase" của ứng dụng thay vì "MapRoute":

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

        // Url shortcuts
        routes.MapRouteLowerCase("Home", "", new { controller = "Home", action = "Index" });
        routes.MapRouteLowerCase("Login", "login", new { controller = "Account", action = "Login" });
        routes.MapRouteLowerCase("Logout", "logout", new { controller = "Account", action = "Logout" });

        routes.MapRouteLowerCase(
            "Default",                                              // Route name
            "{controller}/{action}/{id}",                           // URL with parameters
            new { controller = "Home", action = "Index", id = "" }  // Parameter defaults
        );
    }

Đối với bất kỳ ai đọc điều này, LowercaseRoutelớp trong đoạn mã đầu tiên ở trên dường như đến từ câu trả lời khác này
chue x

6

Điều này thực sự có hai câu trả lời:

  1. Bạn đã có thể làm điều này: công cụ định tuyến thực hiện so sánh không phân biệt chữ hoa chữ thường. Nếu bạn nhập một tuyến đường chữ thường, nó sẽ được chuyển đến bộ điều khiển và hành động thích hợp.
  2. Nếu bạn đang sử dụng các điều khiển tạo liên kết tuyến (ActionLink, RouteLink, v.v.), họ sẽ tạo các liên kết trường hợp hỗn hợp trừ khi bạn ghi đè hành vi mặc định này.

Bạn đang ở một mình cho phần dưới, mặc dù ...


2

Bạn có thể sử dụng thuộc tính ActionName không?

 [ActionName("more_details")]
 public ActionResult MoreDetails(int? page)
 {

 }

Tôi không nghĩ trường hợp quan trọng. More_Details, more_DETAILS, mOrE_DeTaILs trong URL đều đưa bạn đến cùng một Hành động điều khiển.


Tôi đã không thử điều đó - nó sẽ cho phép bạn sử dụng một trong hai? ("Moredetails" hoặc "more_details")
GalacticCowboy

Để theo dõi, tôi đã thử nó và nó yêu cầu bạn sử dụng tên được chỉ định, vì vậy không, nó sẽ không cho phép bạn xử lý theo bất kỳ cách nào. Ngoài ra, tùy thuộc vào cách bạn xây dựng hành động và chế độ xem của bộ điều khiển, bạn có thể cần chỉ định rõ ràng tên của chế độ xem.
GalacticCowboy
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.