2010-02-01 36 views
5

Tôi có trang web ASP.NET MVC nơi tôi muốn các tuyến đường như /{controller}/{id}/{action}/{date}, trong đó "ngày" là mm/dd/yyyy phần của ngày/giờ. (Tôi đang làm việc với dữ liệu thời gian kích thước tương, vì vậy tôi cần cả một ID và một điểm trong thời gian để thực hiện hầu hết các hoạt động)Làm thế nào để làm cho ASP.NET định tuyến thoát khỏi giá trị tuyến đường?

Các tuyến đường cho việc này rất đơn giản:

routes.MapRoute(
    "TimeDimensionedRoute", 
    "{controller}/{id}/{action}/{date}", 
    new { controller = "Iteration", action = "Index", id = String.Empty, date = String.Empty } 
); 

Tuyến đường này bản đồ một cách chính xác " /Foo/100/Chỉnh sửa/01% 2F21% 2F2010 "với hành động mong muốn. Cập nhật: điều này là không chính xác. Điều này KHÔNG được định tuyến đúng, tôi đã nhầm. Xem câu hỏi liên quan được liên kết trong câu trả lời được chấp nhận.

Vấn đề của tôi là khi tôi sử dụng Html.ActionLink() để tạo ra một liên kết cho tuyến đường này, nó không URL mã hóa ngày và tôi kết thúc với URL không hợp lệ như "/Foo/100/Chỉnh sửa/01/21/2010 ".

Có cách nào để cơ sở hạ tầng định tuyến mã hóa các giá trị cho tôi không? Có vẻ như tôi đã phải mã hóa dữ liệu URL theo cách thủ công mà tôi chuyển đến người trợ giúp HTML.

+1

Bạn có thể gửi Html.Sử dụng ActionLink() –

+0

kiểm tra điều này http://stackoverflow.com/questions/6328713/asp-net-mvc-urls-with-slash-in-parameter –

Trả lời

0

Bạn không thể sử dụng một dấu gạch chéo ở một giá trị tuyến đường trong ASP.NET MVC. Ngay cả khi nó được mã hóa URL nó sẽ không hoạt động.

slash in url

Chỉ có một giải pháp nếu bạn sử dụng ASP.NET 4.0

+0

Cảm ơn bạn đã liên kết đến câu hỏi liên quan. Tôi đã tìm kiếm, nhưng không tìm thấy nó. Tuy nhiên, bạn có một giải pháp trong MVC2, nhưng câu trả lời trong câu hỏi được liên kết chỉ hiển thị một giải pháp cho ASP.NET 4.0. –

+0

Tại sao điều này được đánh dấu là câu trả lời khi hiển nhiên nó không phải là câu trả lời? Câu trả lời của TJB là câu trả lời cho điều này, ít nhất, đó là một cách tốt hơn để đi. – mare

+0

Sry Seth bạn nói đúng, bản sửa lỗi chỉ dành cho ASP.NET 4.0. Tôi đã chỉnh sửa câu trả lời của mình. –

4

Tôi đoán rằng nó không tự động url mã hóa nó b/c khó cho trình trợ giúp html để xác định xem bạn có muốn đại diện cho một ngày hay nếu bạn muốn có thêm 3 trường trong tuyến đường, ví dụ:

// Here's what you're seeing 
/Foo /100 /Edit /10/21/2010/ 
// 4 route values 

// But there's know way to know you don't want this 
/Foo /100 /Edit /10 /21 /2010/ 
// 6 route values 

Có lẽ bạn có thể thay đổi lộ trình của bạn là

... 
"{controller}/{id}/{action}/{month}/{day}/{year}", 
... 

Bằng cách đó, nó sẽ luôn luôn làm việc mà không thoát.

Nếu không, bạn có thể làm một URL Encoding của ngày trong Html.ActionLink(...) gọi

+0

Tôi đã nghĩ về việc thay đổi tuyến đường để chấp nhận tháng/ngày/năm. Nó chắc chắn là con người dễ đọc hơn, nhưng tôi muốn các hành động điều khiển của tôi lấy một đối tượng DateTime đơn lẻ, không phải ba giá trị riêng biệt. Sau đó tôi nghĩ về việc viết một mô hình tùy chỉnh chất kết dính để thu hẹp khoảng cách, nhưng điều đó có vẻ như rất nhiều công việc phụ với giá trị đáng ngờ. Cuối cùng tôi chỉ viết một trình trợ giúp lấy phiên bản DateTime và chuyển đổi nó thành "mm-dd-yyyy", hoạt động tốt như một giá trị dữ liệu tuyến đường. –

2

Tôi không biết nếu điều này được tính như một câu trả lời hay không, nhưng tôi luôn sử dụng yyyy-mm-dd định dạng trong URI. Không phải vì slashes được dành riêng cho RFC (mặc dù đó là một lý do chính đáng) nhưng bởi vì nó miễn nhiễm với các vấn đề toàn cầu hóa khi chuyển đổi sang/từ một chuỗi. DateTime.Parse() "chỉ hoạt động" với định dạng này, ngay cả khi ai đó đặt ngôn ngữ máy chủ ở một nơi nào đó ở Đông Âu.

+0

Đó là một ý tưởng hay, cảm ơn! –

1

Tôi gặp vấn đề tương tự vì mã máy khách có thể bao gồm /: và tất cả các loại ký tự. Đây là cách tôi đã giải quyết: http://blog.peterlesliemorris.com/archive/2010/11/19/asp-mvc-encoding-route-values.aspx

Đây là những gì bạn cần làm trong ứng dụng web của mình.

//1: Register a custom value provider in global.asax.cs 
protected void Application_Start() 
{ 
    EncodedRouteValueProviderFactory.Register(); 
    ... 
} 

//2: Use the following code in your views instead of Html.ActionLink 
//this will ensure that all values before the ? query string part of your 
//URL are properly encoded 

<%: Html.EncodedActionLink(.....) %> 
//3: Use this special redirect action when redirecting from a method 
return this.EncodedActionLink(.....); 

Và đây là mã nguồn mở rộng

//EncodedActionLinkExtensions.cs 
using System.Text; 
using System.Text.RegularExpressions; 
using System.Web.Routing; 

namespace System.Web.Mvc.Html 
{ 
    public static class EncodedActionLinkExtensions 
    { 
    public static MvcHtmlString EncodedActionLink(this HtmlHelper htmlHelper, string linkText, string action) 
    { 
     return htmlHelper.EncodedActionLink(linkText, action, (object)null); 
    } 

    public static MvcHtmlString EncodedActionLink(this HtmlHelper htmlHelper, string linkText, string action, string controllerName) 
    { 
     return htmlHelper.EncodedActionLink(linkText, action, controllerName, (object)null); 
    } 

    public static MvcHtmlString EncodedActionLink(this HtmlHelper htmlHelper, string linkText, string action, object explicitRouteValues) 
    { 
     object routeValueObj; 
     if (!htmlHelper.ViewContext.RequestContext.RouteData.Values.TryGetValue("controller", out routeValueObj)) 
     throw new InvalidOperationException("Could not determine controller"); 

     string controllerName = (string)routeValueObj; 
     return htmlHelper.EncodedActionLink(linkText, action, controllerName, explicitRouteValues); 
    } 

    public static MvcHtmlString EncodedActionLink(this HtmlHelper htmlHelper, string linkText, string action, string controllerName, object explicitRouteValues) 
    { 
     return htmlHelper.EncodedActionLink(linkText, action, controllerName, new RouteValueDictionary(explicitRouteValues)); 
    } 

    public static MvcHtmlString EncodedActionLink(this HtmlHelper htmlHelper, string linkText, string action, string controllerName, RouteValueDictionary explicitRouteValues) 
    { 
     string url = EncodedUrlHelper.GenerateUrl(
     htmlHelper.ViewContext.RequestContext, 
     controllerName, action, explicitRouteValues); 
     string result = string.Format("<a href=\"{0}\">{1}</a>", url, linkText); 
     return MvcHtmlString.Create(result); 
    } 
    } 
} 


//EncodedRedirectToRouteExtensions.cs 
using System.Web.Routing; 
namespace System.Web.Mvc 
{ 
    public static class EncodedRedirectToRouteExtensions 
    { 
    public static EncodedRedirectToRouteResult EncodedRedirectToAction(this IController controller, string actionName) 
    { 
     return controller.EncodedRedirectToAction(
     actionName, 
     (string)null, //controllerName, 
     (RouteValueDictionary)null //routeValues 
     ); 
    } 

    public static EncodedRedirectToRouteResult EncodedRedirectToAction(this IController controller, string actionName, object routeValues) 
    { 
     return controller.EncodedRedirectToAction(
     actionName, 
     (string)null, //controllerName, 
     new RouteValueDictionary(routeValues) 
     ); 
    } 

    public static EncodedRedirectToRouteResult EncodedRedirectToAction(this IController controller, string actionName, RouteValueDictionary routeValues) 
    { 
     return controller.EncodedRedirectToAction(
     actionName, 
     (string)null, //controllerName, 
     routeValues 
     ); 
    } 

    public static EncodedRedirectToRouteResult EncodedRedirectToAction(this IController controller, string actionName, string controllerName) 
    { 
     return controller.EncodedRedirectToAction(
     actionName, 
     controllerName, 
     (RouteValueDictionary)null //routeValues 
     ); 
    } 

    public static EncodedRedirectToRouteResult EncodedRedirectToAction(this IController controller, string actionName, string controllerName, object routeValues) 
    { 
     return controller.EncodedRedirectToAction(
     actionName, 
     controllerName, 
     new RouteValueDictionary(routeValues) 
     ); 
    } 

    public static EncodedRedirectToRouteResult EncodedRedirectToAction(this IController controller, string actionName, string controllerName, RouteValueDictionary routeValues) 
    { 
     RouteValueDictionary dictionary; 
     if (routeValues != null) 
     dictionary = new RouteValueDictionary(routeValues); 
     else 
     dictionary = new RouteValueDictionary(); 
     dictionary["controller"] = controllerName; 
     dictionary["action"] = actionName; 

     var result = new EncodedRedirectToRouteResult(dictionary); 
     return result; 
    } 

    } 
} 

//EncodedRedirectToRouteResult.cs 
using System.Web.Mvc; 
using System.Web.Routing; 
namespace System.Web.Mvc 
{ 
    public class EncodedRedirectToRouteResult : ActionResult 
    { 
    readonly string RouteName; 
    readonly RouteValueDictionary RouteValues; 

    public EncodedRedirectToRouteResult(RouteValueDictionary routeValues) 
     : this(null, routeValues) 
    { 
    } 

    public EncodedRedirectToRouteResult(string routeName, RouteValueDictionary routeValues) 
    { 
     RouteName = routeName ?? ""; 
     RouteValues = routeValues != null ? routeValues : new RouteValueDictionary(); 
    } 

    public override void ExecuteResult(ControllerContext context) 
    { 
     string url = EncodedUrlHelper.GenerateUrl(context.RequestContext, null, null, RouteValues); 
     context.Controller.TempData.Keep(); 
     context.HttpContext.Response.Redirect(url, false); 
    } 
    } 
} 

//EncodedRouteValueProvider.cs 
using System.Collections.Generic; 
using System.Text.RegularExpressions; 
using System.Web.Routing; 
using System.Reflection; 
namespace System.Web.Mvc 
{ 
    public class EncodedRouteValueProvider : IValueProvider 
    { 
    readonly ControllerContext ControllerContext; 
    bool Activated = false; 

    public EncodedRouteValueProvider(ControllerContext controllerContext) 
    { 
     ControllerContext = controllerContext; 
    } 

    public bool ContainsPrefix(string prefix) 
    { 
     if (!Activated) 
     DecodeRouteValues(); 
     return false; 
    } 

    public ValueProviderResult GetValue(string key) 
    { 
     if (!Activated) 
     DecodeRouteValues(); 
     return null; 
    } 

    void DecodeRouteValues() 
    { 
     Activated = true; 
     var route = (Route)ControllerContext.RouteData.Route; 
     string url = route.Url; 
     var keysToDecode = new HashSet<string>(); 
     var regex = new Regex(@"\{.+?\}"); 
     foreach (Match match in regex.Matches(url)) 
     keysToDecode.Add(match.Value.Substring(1, match.Value.Length - 2)); 
     foreach (string key in keysToDecode) 
     { 
     object valueObj = ControllerContext.RequestContext.RouteData.Values[key]; 
     if (valueObj == null) 
      continue; 
     string value = valueObj.ToString(); 
     value = UrlValueEncoderDecoder.DecodeString(value); 
     ControllerContext.RouteData.Values[key] = value; 
     ValueProviderResult valueProviderResult = ControllerContext.Controller.ValueProvider.GetValue(key); 
     if (valueProviderResult == null) 
      continue; 
     PropertyInfo attemptedValueProperty = valueProviderResult.GetType().GetProperty("AttemptedValue"); 
     attemptedValueProperty.SetValue(valueProviderResult, value, null); 
     PropertyInfo rawValueProperty = valueProviderResult.GetType().GetProperty("RawValue"); 
     rawValueProperty.SetValue(valueProviderResult, value, null); 
     } 
    } 

    } 
} 

//EncodedRouteValueProviderFactory.cs 
namespace System.Web.Mvc 
{ 
    public class EncodedRouteValueProviderFactory : ValueProviderFactory 
    { 
    public override IValueProvider GetValueProvider(ControllerContext controllerContext) 
    { 
     return new EncodedRouteValueProvider(controllerContext); 
    } 

    public static void Register() 
    { 
     ValueProviderFactories.Factories.Insert(0, new EncodedRouteValueProviderFactory()); 
    } 
    } 
} 

//EncodedUrlHelper.cs 
using System.Text; 
using System.Text.RegularExpressions; 
using System.Web.Mvc; 
namespace System.Web.Routing 
{ 
    public static class EncodedUrlHelper 
    { 
    public static string GenerateUrl(
     RequestContext requestContext, 
     string controllerName, 
     string action, 
     RouteValueDictionary explicitRouteValues) 
    { 
     if (requestContext == null) 
     throw new ArgumentNullException("RequestContext"); 

     var newRouteValues = RouteHelper.GetRouteValueDictionary(
     requestContext, controllerName, action, explicitRouteValues); 
     var route = RouteHelper.GetRoute(requestContext, controllerName, action, newRouteValues); 
     string url = route.Url; 
     //Replace the {values} in the main part of the URL with request values 
     var regex = new Regex(@"\{.+?\}"); 
     url = regex.Replace(url, 
     match => 
     { 
      string key = match.Value.Substring(1, match.Value.Length - 2); 
      object value; 
      if (!newRouteValues.TryGetValue(key, out value)) 
      throw new ArgumentNullException("Cannot reconcile value for key: " + key); 
      string replaceWith; 
      if (value == UrlParameter.Optional) 
      replaceWith = ""; 
      else 
      replaceWith = UrlValueEncoderDecoder.EncodeObject(value); 
      explicitRouteValues.Remove(key); 
      return replaceWith; 
     }); 

     //2: Add additional values after the ? 
     explicitRouteValues.Remove("controller"); 
     explicitRouteValues.Remove("action"); 
     var urlBuilder = new StringBuilder(); 
     urlBuilder.Append("/" + url); 
     string separator = "?"; 
     foreach (var kvp in explicitRouteValues) 
     { 
     if (kvp.Value != UrlParameter.Optional) 
     { 
      urlBuilder.AppendFormat("{0}{1}={2}", separator, kvp.Key, kvp.Value == null ? "" : HttpUtility.UrlEncode(kvp.Value.ToString())); 
      separator = "&"; 
     } 
     } 
     return urlBuilder.ToString(); 
    } 
    } 
} 

//RouteHelper.cs 
namespace System.Web.Routing 
{ 
    public static class RouteHelper 
    { 
    public static RouteValueDictionary GetRouteValueDictionary(
     RequestContext requestContext, 
     string controllerName, 
     string action, 
     RouteValueDictionary explicitRouteValues) 
    { 
     var newRouteValues = new RouteValueDictionary(); 
     var route = GetRoute(requestContext, controllerName, action, explicitRouteValues); 
     MergeValues(route.Defaults, newRouteValues); 
     MergeValues(requestContext.RouteData.Values, newRouteValues); 
     if (explicitRouteValues != null) 
     MergeValues(explicitRouteValues, newRouteValues); 
     if (controllerName != null) 
     newRouteValues["controller"] = controllerName; 
     if (action != null) 
     newRouteValues["action"] = action; 
     return newRouteValues; 
    } 

    public static Route GetRoute(
     RequestContext requestContext, 
     string controllerName, 
     string action, 
     RouteValueDictionary explicitRouteValues 
    ) 
    { 
     var routeValues = new RouteValueDictionary(requestContext.RouteData.Values); 
     if (explicitRouteValues != null) 
     MergeValues(explicitRouteValues, routeValues); 
     if (controllerName != null) 
     routeValues["controller"] = controllerName; 
     if (action != null) 
     routeValues["action"] = action; 
     var virtualPath = RouteTable.Routes.GetVirtualPath(requestContext, routeValues); 
     return (Route)virtualPath.Route; 
    } 

    static void MergeValues(RouteValueDictionary routeValues, RouteValueDictionary result) 
    { 
     foreach (var kvp in routeValues) 
     { 
     if (kvp.Value != null) 
      result[kvp.Key] = kvp.Value; 
     else 
     { 
      object value; 
      if (!result.TryGetValue(kvp.Key, out value)) 
      result[kvp.Key] = null; 
     } 
     } 
    } 
    } 
} 

//UrlValueEncoderDecoder.cs 
using System.Collections.Generic; 
using System.Globalization; 
using System.Text; 
using System.Text.RegularExpressions; 
namespace System.Web.Mvc 
{ 
    public static class UrlValueEncoderDecoder 
    { 
    static HashSet<char> ValidChars; 

    static UrlValueEncoderDecoder() 
    { 
     string chars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ_-."; 
     ValidChars = new HashSet<char>(chars.ToCharArray()); 
    } 

    public static string EncodeObject(object value) 
    { 
     if (value == null) 
     return null; 
     return EncodeString(value.ToString()); 
    } 

    public static string EncodeString(string value) 
    { 
     if (value == null) 
     return null; 
     var resultBuilder = new StringBuilder(); 
     foreach (char currentChar in value.ToCharArray()) 
     if (ValidChars.Contains(currentChar)) 
      resultBuilder.Append(currentChar); 
     else 
     { 
      byte[] bytes = System.Text.UnicodeEncoding.UTF8.GetBytes(currentChar.ToString()); 
      foreach (byte currentByte in bytes) 
      resultBuilder.AppendFormat("${0:x2}", currentByte); 
     } 
     string result = resultBuilder.ToString(); 
     //Special case, use + for spaces as it is shorter and spaces are common 
     return result.Replace("$20", "+"); 
    } 

    public static string DecodeString(string value) 
    { 
     if (value == null) 
     return value; 
     //Special case, change + back to a space 
     value = value.Replace("+", " "); 
     var regex = new Regex(@"\$[0-9a-fA-F]{2}"); 
     value = regex.Replace(value, 
     match => 
     { 
      string hexCode = match.Value.Substring(1, 2); 
      byte byteValue = byte.Parse(hexCode, NumberStyles.AllowHexSpecifier); 
      string decodedChar = System.Text.UnicodeEncoding.UTF8.GetString(new byte[] { byteValue }); 
      return decodedChar; 
     }); 
     return value; 
    } 
    } 
} 
+0

Máy chủ di chuyển, sẽ trở lại sau một hoặc hai ngày. Sau khi sao lưu, tôi cũng sẽ sao chép/dán mã vào câu trả lời. –

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