web-dev-qa-db-fra.com

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

Dans le StackOverflow Podcast # 54 , Jeff mentionne qu'ils enregistrent leurs routes URL dans la base de code StackOverflow via un attribut au-dessus de la méthode qui gère la route. Cela ressemble à un bon concept (avec la mise en garde que Phil Haack a soulevée concernant les priorités de l'itinéraire).

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

De même, des "meilleures pratiques" pour utiliser ce style de routage?

80
TorgoGuy

[~ # ~] mise à jour [~ # ~]: Ceci a été publié sur codeplex . Le code source complet ainsi que l'assembly précompilé sont disponibles en téléchargement. Je n'ai pas encore eu le temps de publier la documentation sur le site, donc ce message SO devra suffire pour l'instant.

[~ # ~] mise à jour [~ # ~]: j'ai ajouté de nouveaux attributs à 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 ne sais pas comment 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 du contrôleur 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 il 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 seul appel à 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 bogues.

62
DSO

Vous pouvez également essayer AttributeRouting , qui est disponible à partir de github ou via nuget .

Ceci est une prise sans vergogne, car je suis l'auteur du projet. Mais si je ne suis pas très content de l'utiliser. Vous pourriez l'être aussi. Il y a beaucoup de documentation et d'exemples de code dans le référentiel github wiki .

Avec cette bibliothèque, vous pouvez faire beaucoup:

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

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

REMARQUE: Depuis le 16/04/12, AttributeRouting prend également en charge la nouvelle infrastructure d'API Web. Juste au cas où vous recherchez quelque chose qui puisse gérer cela . Merci subkamran !

44
spot

1. Téléchargez RiaLibrary.Web.dll et référencez-le 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();
    }
}

BTW, '?' sign in Le paramètre '{? category}' signifie qu'il est facultatif. Vous n'aurez pas besoin de le spécifier explicitement dans les valeurs par défaut de la route, ce qui est égal à ceci:

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

3. Mettre à 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 définir 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();
}
9
Konstantin Tarkus

Ce message est juste pour étendre la réponse de DSO.

Lors de la conversion de mes itinéraires 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 la dénomination de l'itinéraire n'était pas appropriée. Le nom est construit dynamiquement avec controllerName.RouteName. Mais mes noms de route sont des chaînes const dans la classe contrôleur et j'utilise ces const 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 ferai est de convertir les attributs par défaut et de contrainte en AttributeTargets.Parameter afin que je puisse les coller aux paramètres.

3
Nicolas Cadilhac

J'avais besoin de faire fonctionner le routage ITCloud dans asp.net mvc 2 à l'aide d'un AsyncController - pour ce faire, modifiez simplement la classe RouteUtility.cs dans la source et recompilez. Vous devez supprimer "Terminé" du nom de l'action à la ligne 98

// Add to list of routes.
routeParams.Add(new MapRouteParams()
{
    RouteName = String.IsNullOrEmpty(routeAttrib.Name) ? null : routeAttrib.Name,
    Path = routeAttrib.Path,
    ControllerName = controllerName,
    ActionName = methodInfo.Name.Replace("Completed", ""),
    Order = routeAttrib.Order,
    Constraints = GetConstraints(methodInfo),
    Defaults = GetDefaults(methodInfo),
    ControllerNamespace = controllerClass.Namespace,
});

Ensuite, dans AsyncController, décorez le XXXXCompleted ActionResult avec les attributs familiers UrlRoute et UrlRouteParameterDefault:

[UrlRoute(Path = "ActionName/{title}")]
[UrlRouteParameterDefault(Name = "title", Value = "latest-post")]
public ActionResult ActionNameCompleted(string title)
{
    ...
}

J'espère que cela aide quelqu'un avec le même problème.

0
TimDog

J'ai combiné ces deux approches dans une version frankensteinienne pour tous ceux qui le souhaitent. (J'ai aimé la notation param optionnelle, mais j'ai également pensé qu'ils devraient être des attributs distincts de default/contraintes plutôt que tous mélangés en un).

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

0
Max Metral