Conventions de routage dans API Web ASP.NET 2 Odata
Cet article décrit les conventions de routage utilisées par l’API Web 2 dans ASP.NET 4.x pour les points de terminaison OData.
Lorsque l’API web obtient une requête OData, elle mappe la demande à un nom de contrôleur et à un nom d’action. Le mappage est basé sur la méthode HTTP et l’URI. Par exemple, GET /odata/Products(1)
mappe à ProductsController.GetProduct
.
Dans la première partie de cet article, je décrit les conventions de routage OData intégrées. Ces conventions sont conçues spécifiquement pour les points de terminaison OData et remplacent le système de routage d’API web par défaut. (Le remplacement se produit lorsque vous appelez MapODataRoute.)
Dans la partie 2, je montre comment ajouter des conventions de routage personnalisées. Actuellement, les conventions intégrées ne couvrent pas toute la plage des URI OData, mais vous pouvez les étendre pour gérer des cas supplémentaires.
Conventions de routage intégrées
Avant de décrire les conventions de routage OData dans l’API web, il est utile de comprendre les URI OData. Un URI OData se compose des éléments suivants :
- Racine du service
- Chemin d’accès à la ressource
- Options de requête
Pour le routage, la partie importante est le chemin de la ressource. Le chemin de la ressource est divisé en segments. Par exemple, /Products(1)/Supplier
a trois segments :
Products
fait référence à un jeu d’entités nommé « Products ».1
est une clé d’entité, en sélectionnant une seule entité dans l’ensemble.Supplier
est une propriété de navigation qui sélectionne une entité associée.
Ce chemin choisit donc le fournisseur du produit 1.
Notes
Les segments de chemin OData ne correspondent pas toujours aux segments d’URI. Par exemple, « 1 » est considéré comme un segment de chemin d’accès.
Noms des contrôleurs. Le nom du contrôleur est toujours dérivé de l’entité définie à la racine du chemin d’accès de la ressource. Par exemple, si le chemin de la ressource est /Products(1)/Supplier
, l’API web recherche un contrôleur nommé ProductsController
.
Noms des actions. Les noms d’action sont dérivés des segments de chemin d’accès et du modèle de données d’entité (EDM), comme indiqué dans les tableaux suivants. Dans certains cas, vous avez deux choix pour le nom de l’action. Par exemple, « Get » ou « GetProducts ».
Interrogation d’entités
Requête | Exemple d’URI | Nom de l'action | Exemple d’action |
---|---|---|---|
GET /entityset | /Produits | GetEntitySet ou Get | GetProducts |
GET /entityset(key) | /Products(1) | GetEntityType ou Get | GetProduct |
GET /entityset(key)/cast | /Products(1)/Models.Book | GetEntityType ou Get | GetBook |
Pour plus d’informations, consultez Créer un point de terminaison OData Read-Only.
Création, mise à jour et suppression d’entités
Requête | Exemple d’URI | Nom de l'action | Exemple d’action |
---|---|---|---|
POST /entityset | /Produits | PostEntityType ou Post | PostProduct |
PUT /entityset(key) | /Products(1) | PutEntityType ou Put | PutProduct |
PUT /entityset(key)/cast | /Products(1)/Models.Book | PutEntityType ou Put | PutBook |
PATCH /entityset(key) | /Products(1) | PatchEntityType ou Patch | PatchProduct |
PATCH /entityset(key)/cast | /Products(1)/Models.Book | PatchEntityType ou Patch | PatchBook |
DELETE /entityset(key) | /Products(1) | DeleteEntityType ou Delete | DeleteProduct |
DELETE /entityset(key)/cast | /Products(1)/Models.Book | DeleteEntityType ou Delete | DeleteBook |
Interrogation d’une propriété de navigation
Requête | Exemple d’URI | Nom de l'action | Exemple d’action |
---|---|---|---|
GET /entityset(key)/navigation | /Products(1)/Supplier | GetNavigationFromEntityType ou GetNavigation | GetSupplierFromProduct |
GET /entityset(key)/cast/navigation | /Products(1)/Models.Book/Author | GetNavigationFromEntityType ou GetNavigation | GetAuthorFromBook |
Pour plus d’informations, consultez Utilisation des relations d’entité.
Création et suppression de liens
Requête | Exemple d’URI | Nom de l'action |
---|---|---|
POST /entityset(key)/$links/navigation | /Products(1)/$links/Supplier | CreateLink |
PUT /entityset(key)/$links/navigation | /Products(1)/$links/Supplier | CreateLink |
DELETE /entityset(key)/$links/navigation | /Products(1)/$links/Supplier | DeleteLink |
DELETE /entityset(key)/$links/navigation(relatedKey) | /Products/(1)/$links/Suppliers(1) | DeleteLink |
Pour plus d’informations, consultez Utilisation des relations d’entité.
Propriétés
Nécessite l’API web 2
Requête | Exemple d’URI | Nom de l'action | Exemple d’action |
---|---|---|---|
GET /entityset(key)/property | /Products(1)/Name | GetPropertyFromEntityType ou GetProperty | GetNameFromProduct |
GET /entityset(key)/cast/property | /Products(1)/Models.Book/Author | GetPropertyFromEntityType ou GetProperty | GetTitleFromBook |
Actions
Requête | Exemple d’URI | Nom de l'action | Exemple d’action |
---|---|---|---|
POST /entityset(key)/action | /Products(1)/Rate | ActionNameOnEntityType ou ActionName | RateOnProduct |
POST /entityset(key)/cast/action | /Products(1)/Models.Book/CheckOut | ActionNameOnEntityType ou ActionName | CheckOutOnBook |
Pour plus d’informations, consultez Actions OData.
Signatures de méthode
Voici quelques règles pour les signatures de méthode :
- Si le chemin d’accès contient une clé, l’action doit avoir un paramètre nommé key.
- Si le chemin d’accès contient une clé dans une propriété de navigation, l’action doit avoir un paramètre nommé relatedKey.
- Décorez les paramètres key et relatedKey avec le paramètre [FromODataUri].
- Les requêtes POST et PUT prennent un paramètre du type d’entité.
- Les requêtes PATCH prennent un paramètre de type Delta<T>, où T est le type d’entité.
Pour référence, voici un exemple qui montre les signatures de méthode pour chaque convention de routage OData intégrée.
public class ProductsController : ODataController
{
// GET /odata/Products
public IQueryable<Product> Get()
// GET /odata/Products(1)
public Product Get([FromODataUri] int key)
// GET /odata/Products(1)/ODataRouting.Models.Book
public Book GetBook([FromODataUri] int key)
// POST /odata/Products
public HttpResponseMessage Post(Product item)
// PUT /odata/Products(1)
public HttpResponseMessage Put([FromODataUri] int key, Product item)
// PATCH /odata/Products(1)
public HttpResponseMessage Patch([FromODataUri] int key, Delta<Product> item)
// DELETE /odata/Products(1)
public HttpResponseMessage Delete([FromODataUri] int key)
// PUT /odata/Products(1)/ODataRouting.Models.Book
public HttpResponseMessage PutBook([FromODataUri] int key, Book item)
// PATCH /odata/Products(1)/ODataRouting.Models.Book
public HttpResponseMessage PatchBook([FromODataUri] int key, Delta<Book> item)
// DELETE /odata/Products(1)/ODataRouting.Models.Book
public HttpResponseMessage DeleteBook([FromODataUri] int key)
// GET /odata/Products(1)/Supplier
public Supplier GetSupplierFromProduct([FromODataUri] int key)
// GET /odata/Products(1)/ODataRouting.Models.Book/Author
public Author GetAuthorFromBook([FromODataUri] int key)
// POST /odata/Products(1)/$links/Supplier
public HttpResponseMessage CreateLink([FromODataUri] int key,
string navigationProperty, [FromBody] Uri link)
// DELETE /odata/Products(1)/$links/Supplier
public HttpResponseMessage DeleteLink([FromODataUri] int key,
string navigationProperty, [FromBody] Uri link)
// DELETE /odata/Products(1)/$links/Parts(1)
public HttpResponseMessage DeleteLink([FromODataUri] int key, string relatedKey, string navigationProperty)
// GET odata/Products(1)/Name
// GET odata/Products(1)/Name/$value
public HttpResponseMessage GetNameFromProduct([FromODataUri] int key)
// GET /odata/Products(1)/ODataRouting.Models.Book/Title
// GET /odata/Products(1)/ODataRouting.Models.Book/Title/$value
public HttpResponseMessage GetTitleFromBook([FromODataUri] int key)
}
Conventions de routage personnalisées
Actuellement, les conventions intégrées ne couvrent pas tous les URI OData possibles. Vous pouvez ajouter de nouvelles conventions en implémentant l’interface IODataRoutingConvention . Cette interface a deux méthodes :
string SelectController(ODataPath odataPath, HttpRequestMessage request);
string SelectAction(ODataPath odataPath, HttpControllerContext controllerContext,
ILookup<string, HttpActionDescriptor> actionMap);
- SelectController retourne le nom du contrôleur.
- SelectAction retourne le nom de l’action.
Pour les deux méthodes, si la convention ne s’applique pas à cette demande, la méthode doit retourner null.
Le paramètre ODataPath représente le chemin de la ressource OData analysé. Il contient une liste d’instances ODataPathSegment , une pour chaque segment du chemin de la ressource. ODataPathSegment est une classe abstraite ; chaque type de segment est représenté par une classe qui dérive de ODataPathSegment.
La propriété ODataPath.TemplatePath est une chaîne qui représente la concaténation de tous les segments de chemin d’accès. Par exemple, si l’URI est /Products(1)/Supplier
, le modèle de chemin est « ~/entityset/key/navigation ». Notez que les segments ne correspondent pas directement aux segments d’URI. Par exemple, la clé d’entité (1) est représentée sous la forme de son propre ODataPathSegment.
En règle générale, une implémentation d’IODataRoutingConvention effectue les opérations suivantes :
- Comparez le modèle de chemin d’accès pour voir si cette convention s’applique à la requête actuelle. Si elle ne s’applique pas, retournez la valeur Null.
- Si la convention s’applique, utilisez les propriétés des instances ODataPathSegment pour dériver des noms de contrôleur et d’action.
- Pour les actions, ajoutez des valeurs au dictionnaire de routes qui doivent être liées aux paramètres d’action (généralement des clés d’entité).
Examinons un exemple spécifique. Les conventions de routage intégrées ne prennent pas en charge l’indexation dans une collection de navigation. En d’autres termes, il n’existe aucune convention pour les URI comme suit :
/odata/Products(1)/Suppliers(1)
Voici une convention de routage personnalisée pour gérer ce type de requête.
using Microsoft.Data.Edm;
using System.Linq;
using System.Net.Http;
using System.Web.Http.Controllers;
using System.Web.Http.OData.Routing;
using System.Web.Http.OData.Routing.Conventions;
namespace ODataRouting
{
public class NavigationIndexRoutingConvention : EntitySetRoutingConvention
{
public override string SelectAction(ODataPath odataPath, HttpControllerContext context,
ILookup<string, HttpActionDescriptor> actionMap)
{
if (context.Request.Method == HttpMethod.Get &&
odataPath.PathTemplate == "~/entityset/key/navigation/key")
{
NavigationPathSegment navigationSegment = odataPath.Segments[2] as NavigationPathSegment;
IEdmNavigationProperty navigationProperty = navigationSegment.NavigationProperty.Partner;
IEdmEntityType declaringType = navigationProperty.DeclaringType as IEdmEntityType;
string actionName = "Get" + declaringType.Name;
if (actionMap.Contains(actionName))
{
// Add keys to route data, so they will bind to action parameters.
KeyValuePathSegment keyValueSegment = odataPath.Segments[1] as KeyValuePathSegment;
context.RouteData.Values[ODataRouteConstants.Key] = keyValueSegment.Value;
KeyValuePathSegment relatedKeySegment = odataPath.Segments[3] as KeyValuePathSegment;
context.RouteData.Values[ODataRouteConstants.RelatedKey] = relatedKeySegment.Value;
return actionName;
}
}
// Not a match.
return null;
}
}
}
Remarques :
- Je dérive d’EntitySetRoutingConvention, car la méthode SelectController de cette classe est appropriée pour cette nouvelle convention de routage. Cela signifie que je n’ai pas besoin de ré-implémenter SelectController.
- La convention s’applique uniquement aux requêtes GET, et uniquement lorsque le modèle de chemin est « ~/entityset/key/navigation/key ».
- Le nom de l’action est « Get{EntityType} », où {EntityType} est le type de la collection de navigation. Par exemple, « GetSupplier ». Vous pouvez utiliser n’importe quelle convention d’affectation de noms de votre choix. Assurez-vous simplement que les actions de votre contrôleur correspondent.
- L’action prend deux paramètres nommés key et relatedKey. (Pour obtenir la liste de certains noms de paramètres prédéfinis, consultez ODataRouteConstants.)
L’étape suivante consiste à ajouter la nouvelle convention à la liste des conventions de routage. Cela se produit pendant la configuration, comme indiqué dans le code suivant :
using ODataRouting.Models;
using System.Web.Http;
using System.Web.Http.OData.Builder;
using System.Web.Http.OData.Routing;
using System.Web.Http.OData.Routing.Conventions;
namespace ODataRouting
{
public static class WebApiConfig
{
public static void Register(HttpConfiguration config)
{
ODataModelBuilder modelBuilder = new ODataConventionModelBuilder();
// Create EDM (not shown).
// Create the default collection of built-in conventions.
var conventions = ODataRoutingConventions.CreateDefault();
// Insert the custom convention at the start of the collection.
conventions.Insert(0, new NavigationIndexRoutingConvention());
config.Routes.MapODataRoute(routeName: "ODataRoute",
routePrefix: "odata",
model: modelBuilder.GetEdmModel(),
pathHandler: new DefaultODataPathHandler(),
routingConventions: conventions);
}
}
}
Voici d’autres exemples de conventions de routage qui sont utiles à étudier :
Bien sûr, l’API web elle-même étant open source, vous pouvez voir le code source pour les conventions de routage intégrées. Elles sont définies dans l’espace de noms System.Web.Http.OData.Routing.Conventions .