2010-02-01 3 views
5

J'ai un site ASP.NET MVC où je veux des routes comme /{controller}/{id}/{action}/{date}, où "date" est la partie mm/jj/aaaa d'une date/heure. (Je traite des données dimensionnées temps, donc je besoin à la fois un identifiant et un point dans le temps de faire la plupart des opérations)Comment faire ASP.NET Routage des valeurs de route d'échappement?

La route est simple:

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

Cet itinéraire correctement les cartes " /Foo/100/Edit/01% 2F21% 2F2010 "à l'action désirée. Mise à jour: c'est incorrect. Ce n'est pas routé correctement, je me suis trompé. Voir la question connexe liée à la réponse acceptée.

Mon problème est que lorsque j'utilise Html.ActionLink() pour générer un lien pour cette voie, il ne URL encode la date et je finis avec des URL non valides telles que "/Foo/100/Edit/01/21/2010 ".

Existe-t-il un moyen d'obtenir l'encodage de l'infrastructure de routage pour moi? Il semble erroné que je doive coder manuellement les données que je passe aux helpers HTML.

+1

Pouvez-vous poster le Html.ActionLink() utilisation –

+0

vérifier cela http://stackoverflow.com/questions/6328713/asp-net-mvc-urls-with-slash-in-parameter –

Répondre

0

Vous ne pouvez pas utiliser une barre oblique dans une valeur de route dans ASP.NET MVC. Même s'il est codé en URL, cela ne fonctionnera pas.

slash in url

Il n'y a qu'une solution si vous utilisez ASP.NET 4.0

+0

Merci pour le lien à la question connexe. J'ai cherché, mais je ne l'ai pas trouvé. Toutefois, vous déclarez qu'il existe une solution dans MVC2, mais les réponses dans la question liée montrent uniquement une solution de contournement pour ASP.NET 4.0. –

+0

pourquoi est-ce marqué comme une réponse quand il est évident que ce n'est pas? La réponse de TJB est plus une réponse à cela, au moins, c'est une meilleure voie à suivre. – mare

+0

Sry Seth vous avez raison, le correctif est pour ASP.NET 4.0 seulement. J'ai édité ma réponse. –

4

Je suppose qu'il n'encode pas automatiquement l'encodage b/c c'est difficile pour l'assistant html de déterminer si vous voulez représenter une date ou si vous voulez avoir 3 champs de plus dans la route par exemple.

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

Peut-être que vous pourriez modifier votre itinéraire pour être

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

De cette façon, il serait toujours travailler sans échapper.

Sinon, vous pourriez faire un URL Encoding la date dans le Html.ActionLink(...) appel

+0

J'ai pensé à changer la route pour accepter mois/jour/année. C'est certainement plus lisible par l'homme, mais je veux que mes actions de contrôleur prennent un seul objet DateTime, pas trois valeurs séparées. J'ai alors pensé à écrire un classeur de modèle fait sur commande pour combler l'écart, mais cela a semblé beaucoup de travail supplémentaire avec la valeur douteuse. À la fin, j'ai juste écrit une aide qui prend l'instance DateTime et la convertit en "mm-jj-aaaa", ce qui fonctionne bien comme valeur de données de route. –

2

Je ne sais pas si cela compte comme une réponse ou non, mais je toujours utilisez le format yyyy-mm-dd dans URIs. Pas parce que les barres obliques sont réservées par RFC (bien que ce soit une bonne raison), mais parce qu'il est immunisé contre les problèmes de globalisation lors de la conversion vers/depuis une chaîne. DateTime.Parse() "fonctionne" avec ce format, même si quelqu'un définit les paramètres régionaux du serveur quelque part en Europe de l'Est.

+0

C'est une bonne idée, merci! –

1

J'ai rencontré le même problème car les codes clients pouvaient inclure /: et toutes sortes de caractères. Voici comment je l'ai résolu: http://blog.peterlesliemorris.com/archive/2010/11/19/asp-mvc-encoding-route-values.aspx

C'est ce que vous devez faire dans votre application web.

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

Et ceci est le code source d'extension

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

Serveur de migration, devrait être de retour dans un jour ou deux. Une fois qu'il est sauvegardé, je vais copier/coller le code dans la réponse aussi. –

Questions connexes