2010-02-01 9 views
5

Ich habe eine ASP.NET MVC-Site, wo ich Routen wie /{controller}/{id}/{action}/{date}, wo "Datum" ist der mm/TT/JJJJ Teil eines Datums/Zeit. (Ich bin mit der Zeit dimensionierten Daten handelt, also muss ich sowohl eine ID und ein Punkt in der Zeit die meisten Operationen zu tun)Wie man ASP.NET Routing Fluchtwege Werte machen?

Der Weg dafür ist einfach:

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

Diese Route korrekt abbildet " /Foo/100/Bearbeiten/01% 2F21% 2F2010 "auf die gewünschte Aktion. Update: das ist falsch. Das ist NICHT richtig geroutet, ich habe mich geirrt. Siehe die verwandte Frage, die in der akzeptierten Antwort verlinkt ist.

Mein Problem ist, dass, wenn ich Html.ActionLink() verwenden Sie einen Link auf dieser Route zu erzeugen, ist es hat keine URL-Codierung das Datum und ich mit ungültigen URLs am Ende wie "/Foo/100/Edit/01/21/2010 ".

Gibt es eine Möglichkeit, die Routing-Infrastruktur zu erhalten, um die Werte für mich zu kodieren? Es scheint falsch, dass ich Daten, die ich an die HTML-Helfer weitergebe, manuell URL-kodieren muss.

+1

können Sie die HTML schreiben.ActionLink() Verwendung –

+0

check this out http://stackoverflow.com/questions/6328713/asp-net-mvc-urls-with-slash-in-parameter –

Antwort

0

Sie können nicht mit einem Schrägstrich in einer Route Wert in ASP.NET MVC verwenden. Selbst wenn es URL-codiert ist, wird es nicht funktionieren.

slash in url

Es gibt nur eine Lösung, wenn Sie ASP.NET 4.0 verwenden

+0

Danke für den Link zu der verwandten Frage. Ich suchte, fand es aber nicht. Sie geben jedoch an, dass es eine Lösung in MVC2 gibt, aber die Antworten in der verknüpften Frage zeigen nur eine Problemumgehung für ASP.NET 4.0. –

+0

Warum ist dies als eine Antwort markiert, wenn es offensichtlich ist, ist es nicht? Die Antwort von TJB ist mehr eine Antwort darauf, zumindest ist es ein besserer Weg zu gehen. – mare

+0

Sry Seth, Sie haben Recht, das Update ist nur für ASP.NET 4.0. Ich habe meine Antwort bearbeitet. –

4

Ich vermute, dass es nicht automatisch url codiert es b/c ist es schwer für den HTML-Helfer zu bestimmen, ob Sie ein Datum darstellen möchten oder wenn Sie 3 weitere Felder in der Route haben möchten, z.

// 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 

Vielleicht könnten Sie Ihre Route

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

Auf diese Weise werden zu ändern, wäre es immer arbeiten, ohne zu entkommen.

Andernfalls könnten Sie eine URL Encoding des Datums innerhalb des Html.ActionLink(...) Telefonierens

+0

Ich dachte über die Änderung der Route, um Monat/Tag/Jahr zu akzeptieren. Es ist sicherlich menschlicher lesbar, aber ich möchte meine Controller-Aktionen ein einzelnes DateTime-Objekt, nicht drei separate Werte. Ich dachte dann darüber nach, ein benutzerdefiniertes Modellbuch zu schreiben, um die Lücke zu überbrücken, aber das schien eine Menge zusätzlicher Arbeit mit zweifelhaftem Wert zu sein. Am Ende habe ich nur einen Helfer geschrieben, der die DateTime-Instanz übernimmt und sie in "mm-dd-yyyy" konvertiert, was als Routendatenwert gut funktioniert. –

2

Ich weiß nicht, ob dies als eine Antwort zählt oder nicht, aber ich immer Verwendung yyyy-mm-dd Format in URIs. Nicht, weil Schrägstriche per RFC reserviert sind (obwohl das ein guter Grund ist), sondern weil sie immun gegenüber Globalisierungsproblemen sind, wenn sie in eine Zeichenfolge konvertiert werden. DateTime.Parse() "funktioniert einfach" mit diesem Format, auch wenn jemand das Server-Gebietsschema irgendwo in Osteuropa setzt.

+0

Das ist eine gute Idee, danke! –

1

Ich hatte das gleiche Problem, weil Client-Codes /: und alle Arten von Zeichen enthalten können. So habe ich es gelöst: http://blog.peterlesliemorris.com/archive/2010/11/19/asp-mvc-encoding-route-values.aspx

Dies ist, was Sie in Ihrer Web-App tun müssen.

//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(.....); 

Und dies ist die Erweiterung Quellcode

//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

Server migrieren, sollte in ein oder zwei Tagen zurück sein. Sobald es wieder da ist, werde ich den Code in die Antwort kopieren und einfügen. –