54 votes

ASP.NET MVC Url.Action ajoute les valeurs de la route actuelle à l'url générée

J'ai vu cette question plusieurs fois ici, dans l'OS, mais aucune fois avec une réponse acceptable :

ASP.NET MVC @Url.Action inclut les données de l'itinéraire actuel
ASP.NET MVC ajoute implicitement des valeurs de route

En gros, j'ai un contrôleur avec une méthode d'action appelée Groupe, il a une surcharge qui ne reçoit aucun paramètre et affiche une liste d'éléments et une autre qui reçoit un id et affiche les détails de ce groupe.

Si je fais quelque chose comme ça :

Url.Action("Group", "Groups");

Depuis la page principale du site (/), il renvoie une url comme celle-ci :

"mysite.com/Groups/Group"

ce qui est correct Maintenant, si l'adresse actuelle du site est /Groups/Group/1 et que j'appelle la même méthode

Url.Action("Group", "Groups");

l'url retournée est la suivante :

"mysite.com/Groups/Group/1"

Il ajoute automatiquement la valeur de la route pour la page actuelle lors de la génération de l'URL. Même si je génère l'URL de cette façon :

Url.Action("Group", "Groups", null);

Ainsi, en spécifiant explicitement que je ne veux pas de valeurs de route, l'URL générée est la même. Pour obtenir l'adresse que je veux, je dois explicitement définir la valeur de route à une chaîne vide, comme ceci :

Url.Action("Group", "Groups", new {id=""});

Cela va générer l'url suivante :

"mysite.com/Groups/Group"

Ma question est la suivante : pourquoi cela se produit-il ? Si je ne définis aucune valeur de route, elle ne devrait pas les ajouter à l'URL générée.

51voto

objectbox Points 886

Url.Action réutilisera les paramètres de la requête en cours, si vous ne les définissez pas explicitement. C'est par conception dans l'algorithme de correspondance d'url sortant. Lors de la recherche des paramètres de données de route dans un processus de génération d'url, les paramètres sont pris à partir de :

1) les valeurs explicitement fournies

2) les valeurs de la demande actuelle

3) les défauts

Dans l'ordre que j'ai spécifié ci-dessus.

L'algorithme de correspondance des routes sortantes est compliqué, il est donc recommandé de définir explicitement tous les paramètres de la requête, comme vous l'avez fait dans votre exemple.

2voto

broadband Points 521

Un exemple simple :

public class ProductController : Controller
{
  public ActionResult Edit(int id)
  {
    return View();
  }

  [Route("Product/Detail/{id:int}")]
  public ActionResult Detail(int id)
  {
    return View();
  }
}

La vue d'édition ne contient que ceci :

@{ Layout = null;}
@Url.Action("Detail", "Cmr")

Ainsi, lorsque vous exécutez votre site, par exemple. localhost:randomPort/Product/Edit/123 vous obtenez la prochaine réponse : /Product/Detail/123

Pourquoi ? Parce que Route a un paramètre obligatoire id . Le paramètre Id est lu depuis l'url, bien que nous ayons écrit seulement Url.Action(methodName, controller) - sans spécifier de paramètre. De même, cela n'a pas de sens d'avoir un détail de méthode sans id.

Pour que les attributs fonctionnent, la ligne suivante doit être ajoutée à l'adresse suivante RouteConfig.cs :

public static void RegisterRoutes(RouteCollection routes)
{
  ...
  routes.MapMvcAttributeRoutes();
  ...
}

2voto

Valamas - AUS Points 8359

Mon application définit explicitement les valeurs de la route et ne veut pas d'une valeur magique de la demande actuelle. Je veux avoir un contrôle total.

J'ai réalisé une extension qui coexiste avec ma collection de bibliothèque de route. D'où le paramètre unique RouteValueDictionary. (Voir mon commentaire sur la bibliothèque Route en bas de page)

Ici, je supprime toute valeur de route de la requête avant de générer une url.

(note : pour la partie ignorant la casse de array.contains, voir : Comment rendre Array.Contains insensible à la casse sur un tableau de chaînes de caractères ? )

public static string Action(this UrlHelper helper, 
                            RouteValueDictionary routeValues)
{
    RemoveRoutes(helper.RequestContext.RouteData.Values);

    string url = helper.Action(routeValues["Action"].ToString(), routeValues);
    return url;
}

public static void RemoveRoutes(RouteValueDictionary currentRouteData)
{
    List<string> keyList = new List<string>(currentRouteData.Keys);

    string[] ignore = new[] { "Area", "Controller", "Action" };
    foreach (string key in keyList)
    {
        if (!ignore.Contains(key, StringComparer.CurrentCultureIgnoreCase))
            currentRouteData.Remove(key);
    }
}

J'ai des méthodes d'extension Form et ActionLink qui utilisent la méthode RemoveRoutes. Aucun helper de ma bibliothèque mvc n'utilise une méthode qui n'est pas une méthode d'extension que j'ai créée. Ainsi, toutes les données de routage sont nettoyées avant de générer les urls.

Pour la référence, j'utilise AttributeRouting. Voici un exemple d'une route de ma bibliothèque de routes.

public static RouteValueDictionary DisplayNews(int newsId)
{
    RouteValueDictionary route = new RouteValueDictionary();
    route["Area"] = _area;
    route["Controller"] = _controller;
    route["Action"] = "DisplayNews";
    route["newsId"] = newsId;
    return route;
}

2voto

xuser Points 160

Donc quand j'ai lu la réponse d'objectbox, j'ai pensé que je devais modifier plusieurs liens dans mon code. J'ai alors essayé d'ajouter une route par défaut en omettant les paramètres par défaut, ce qui a résolu le problème :

routes.MapRoute(
    "ArtistArtworkDefPage",
    "Artist/{username}/Artwork",
    new
    {
        controller = "Artist",
        action = "Artwork",
        page = 1
    }
);

routes.MapRoute(
    "ArtistArtwork",
    "Artist/{username}/Artwork/{page}",
    new
    {
        controller = "Artist",
        action = "Artwork",
        page = 1
    },
    new { page = @"\d+" }
);

1voto

Mahdi Fathalla Points 31

J'ai trouvé une solution de contournement stupide qui permet de gérer les données de routage de la page actuelle, y compris de modifier le paramètre de la page en fonction de vos préférences.

@{
    var current_route_1 = new RouteValueDictionary(Url.RequestContext.RouteData.Values);
    var current_route_2 = new RouteValueDictionary(Url.RequestContext.RouteData.Values);

    //If you want to customize the routing values
    current_route_1["controller"] = "Controller1";
    current_route_2["controller"] = "Controller2";
}

@Url.RouteUrl(current_route_1);
@Url.RouteUrl(current_route_2);

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X