80 votes

ASP.NET MVC Routage via les attributs de méthode

Dans le Podcast StackOverflow n°54 Dans son article, Jeff mentionne qu'ils enregistrent leurs routes URL dans la base de code de StackOverflow via un attribut au-dessus de la méthode qui gère la route. Cela semble être un bon concept (avec la mise en garde de Phil Haack concernant les priorités des routes).

Quelqu'un pourrait-il fournir un exemple pour que cela se produise ?

Par ailleurs, existe-t-il des "meilleures pratiques" pour utiliser ce style de routage ?

62voto

DSO Points 5942

UPDATE : Ceci a été posté sur codeplex . Le code source complet ainsi que l'assemblage pré-compilé sont disponibles en téléchargement. Je n'ai pas encore eu le temps de poster la documentation sur le site, donc ce message de l'OS devra suffire pour le moment.

UPDATE : J'ai ajouté quelques nouveaux attributs pour gérer 1) l'ordre des routes, 2) les contraintes des paramètres de route, et 3) les valeurs par défaut des paramètres de route. Le texte ci-dessous reflète cette mise à jour.

J'ai en fait fait quelque chose comme ça pour mes projets MVC (je n'ai aucune idée de la façon dont Jeff le fait avec stackoverflow). J'ai défini un ensemble d'attributs personnalisés : UrlRoute, UrlRouteParameterConstraint, UrlRouteParameterDefault. Ils peuvent être attachés aux méthodes d'action des contrôleurs MVC pour que les routes, les contraintes et les valeurs par défaut leur soient automatiquement liées.

Exemple d'utilisation :

(Notez que cet exemple est quelque peu artificiel mais qu'il démontre la fonctionnalité).

public class UsersController : Controller
{
    // Simple path.
    // Note you can have multiple UrlRoute attributes affixed to same method.
    [UrlRoute(Path = "users")]
    public ActionResult Index()
    {
        return View();
    }

    // Path with parameter plus constraint on parameter.
    // You can have multiple constraints.
    [UrlRoute(Path = "users/{userId}")]
    [UrlRouteParameterConstraint(Name = "userId", Regex = @"\d+")]
    public ActionResult UserProfile(int userId)
    {
        // ...code omitted

        return View();
    }

    // Path with Order specified, to ensure it is added before the previous
    // route.  Without this, the "users/admin" URL may match the previous
    // route before this route is even evaluated.
    [UrlRoute(Path = "users/admin", Order = -10)]
    public ActionResult AdminProfile()
    {
        // ...code omitted

        return View();
    }

    // Path with multiple parameters and default value for the last
    // parameter if its not specified.
    [UrlRoute(Path = "users/{userId}/posts/{dateRange}")]
    [UrlRouteParameterConstraint(Name = "userId", Regex = @"\d+")]
    [UrlRouteParameterDefault(Name = "dateRange", Value = "all")]
    public ActionResult UserPostsByTag(int userId, string dateRange)
    {
        // ...code omitted

        return View();
    }

Définition de UrlRouteAttribute :

/// <summary>
/// Assigns a URL route to an MVC Controller class method.
/// </summary>
[AttributeUsage(AttributeTargets.Method, Inherited = true, AllowMultiple = true)]
public class UrlRouteAttribute : Attribute
{
    /// <summary>
    /// Optional name of the route.  If not specified, the route name will
    /// be set to [controller name].[action name].
    /// </summary>
    public string Name { get; set; }

    /// <summary>
    /// Path of the URL route.  This is relative to the root of the web site.
    /// Do not append a "/" prefix.  Specify empty string for the root page.
    /// </summary>
    public string Path { get; set; }

    /// <summary>
    /// Optional order in which to add the route (default is 0).  Routes
    /// with lower order values will be added before those with higher.
    /// Routes that have the same order value will be added in undefined
    /// order with respect to each other.
    /// </summary>
    public int Order { get; set; }
}

Définition de UrlRouteParameterConstraintAttribute :

/// <summary>
/// Assigns a constraint to a route parameter in a UrlRouteAttribute.
/// </summary>
[AttributeUsage(AttributeTargets.Method, Inherited = true, AllowMultiple = true)]
public class UrlRouteParameterConstraintAttribute : Attribute
{
    /// <summary>
    /// Name of the route parameter on which to apply the constraint.
    /// </summary>
    public string Name { get; set; }

    /// <summary>
    /// Regular expression constraint to test on the route parameter value
    /// in the URL.
    /// </summary>
    public string Regex { get; set; }
}

Définition de UrlRouteParameterDefaultAttribute :

/// <summary>
/// Assigns a default value to a route parameter in a UrlRouteAttribute
/// if not specified in the URL.
/// </summary>
[AttributeUsage(AttributeTargets.Method, Inherited = true, AllowMultiple = true)]
public class UrlRouteParameterDefaultAttribute : Attribute
{
    /// <summary>
    /// Name of the route parameter for which to supply the default value.
    /// </summary>
    public string Name { get; set; }

    /// <summary>
    /// Default value to set on the route parameter if not specified in the URL.
    /// </summary>
    public object Value { get; set; }
}

Modifications apportées à Global.asax.cs :

Remplacez les appels à MapRoute, par un appel unique à la fonction RouteUtility.RegisterUrlRoutesFromAttributes :

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

        RouteUtility.RegisterUrlRoutesFromAttributes(routes);
    }

Définition de RouteUtility.RegisterUrlRoutesFromAttributes :

La source complète est sur codeplex . Veuillez vous rendre sur le site si vous avez des commentaires ou des rapports de bogue.

44voto

spot Points 2003

Vous pouvez également essayer AttributRouting qui est disponible auprès de github ou via nuget .

Il s'agit d'une publicité éhontée, puisque je suis l'auteur du projet. Mais bon sang, je ne suis pas très heureux de l'utiliser. Vous pourriez l'être aussi. Il y a beaucoup de documentation et d'exemples de code dans le dépôt github. wiki .

Avec cette bibliothèque, vous pouvez faire beaucoup de choses :

  • Décorez vos actions avec des attributs GET, POST, PUT et DELETE.
  • Associez plusieurs itinéraires à une seule action, en les ordonnant à l'aide d'une propriété Order.
  • Spécifiez les valeurs par défaut et les contraintes des routes à l'aide d'attributs.
  • Spécifiez les paramètres optionnels avec un simple jeton ? devant le nom du paramètre.
  • Spécifiez le nom de la route pour la prise en charge des routes nommées.
  • Définir les zones MVC sur un contrôleur ou un contrôleur de base.
  • Regroupez ou imbriquez vos routes en utilisant des préfixes de route appliqués à un contrôleur ou à un contrôleur de base.
  • Prise en charge des anciennes urls.
  • Définir la priorité des routes parmi les routes définies pour une action, au sein d'un contrôleur, et parmi les contrôleurs et les contrôleurs de base.
  • Générer automatiquement des urls sortantes en minuscules.
  • Définissez vos propres conventions de routage personnalisées et appliquez-les à un contrôleur afin de générer les itinéraires pour les actions au sein du contrôleur sans attributs passe-partout (pensez au style RESTful).
  • Déboguez vos routes en utilisant un HttpHandler fourni.

Je suis sûr qu'il y a d'autres choses que j'oublie. Vérifiez-le. Il est facile à installer via nuget.

REMARQUE : Depuis le 16 avril 2012, AttributeRouting prend également en charge la nouvelle infrastructure API Web. Juste au cas où vous chercheriez quelque chose qui puisse gérer ça. Merci Subkamran !

9voto

Konstantin Tarkus Points 16862

1. Télécharger RiaLibrary.Web.dll et le référencer dans votre projet de site web ASP.NET MVC

2. Décorez les méthodes du contrôleur avec les attributs [Url] :

public SiteController : Controller
{
    [Url("")]
    public ActionResult Home()
    {
        return View();
    }

    [Url("about")]
    public ActionResult AboutUs()
    {
        return View();
    }

    [Url("store/{?category}")]
    public ActionResult Products(string category = null)
    {
        return View();
    }
}

Par ailleurs, le signe " ?" dans le paramètre "{?category}" signifie qu'il est facultatif. Vous n'aurez pas besoin de le spécifier explicitement dans les paramètres par défaut des routes, qui sont égaux à celui-ci :

routes.MapRoute("Store", "store/{category}",
new { controller = "Store", action = "Home", category = UrlParameter.Optional });

3. Mettez à jour le fichier Global.asax.cs

public class MvcApplication : System.Web.HttpApplication
{
    public static void RegisterRoutes(RouteCollection routes)
    {
        routes.IgnoreRoute("{resource}.axd/{*pathInfo}");

        routes.MapRoutes(); // This does the trick
    }

    protected void Application_Start()
    {
        RegisterRoutes(RouteTable.Routes);
    }
}

Comment définir les valeurs par défaut et les contraintes ? Exemple :

public SiteController : Controller
{
    [Url("admin/articles/edit/{id}", Constraints = @"id=\d+")]
    public ActionResult ArticlesEdit(int id)
    {
        return View();
    }

    [Url("articles/{category}/{date}_{title}", Constraints =
         "date=(19|20)\d\d-(0[1-9]|1[012])-(0[1-9]|[12][0-9]|3[01])")]
    public ActionResult Article(string category, DateTime date, string title)
    {
        return View();
    }
}

Comment régler la commande ? Exemple :

[Url("forums/{?category}", Order = 2)]
public ActionResult Threads(string category)
{
    return View();
}

[Url("forums/new", Order = 1)]
public ActionResult NewThread()
{
    return View();
}

3voto

Nicolas Cadilhac Points 2732

Ce message a pour but de prolonger la réponse de DSO.

En convertissant mes routes en attributs, j'avais besoin de gérer l'attribut ActionName. Ainsi, dans GetRouteParamsFromAttribute :

ActionNameAttribute anAttr = methodInfo.GetCustomAttributes(typeof(ActionNameAttribute), false)
    .Cast<ActionNameAttribute>()
    .SingleOrDefault();

// Add to list of routes.
routeParams.Add(new MapRouteParams()
{
    RouteName = routeAttrib.Name,
    Path = routeAttrib.Path,
    ControllerName = controllerName,
    ActionName = (anAttr != null ? anAttr.Name : methodInfo.Name),
    Order = routeAttrib.Order,
    Constraints = GetConstraints(methodInfo),
    Defaults = GetDefaults(methodInfo),
});

J'ai également trouvé que le nom de l'itinéraire n'était pas approprié. Le nom est construit dynamiquement avec controllerName.RouteName. Mais mes noms de route sont des chaînes constantes dans la classe du contrôleur et j'utilise ces constantes pour appeler également Url.RouteUrl. C'est pourquoi j'ai vraiment besoin que le nom de la route dans l'attribut soit le nom réel de la route.

Une autre chose que je vais faire est de convertir les attributs default et constraint en AttributeTargets.Parameter afin de pouvoir les coller aux params.

0voto

Max Metral Points 31

J'ai combiné ces deux approches en une version Frankensteinienne pour tous ceux qui le souhaitent. (J'ai aimé la notation des paramètres optionnels, mais j'ai aussi pensé qu'ils devraient être des attributs séparés de default/constraints plutôt que d'être tous mélangés en un seul).

http://github.com/djMax/AlienForce/tree/master/Utilities/Web/

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