OData - ASP.NET Web API 2 中Odata路由约定

来源:互联网 发布:数据迁移服务 编辑:程序博客网 时间:2024/06/05 05:45

原文地址:http://www.asp.net/web-api/overview/odata-support-in-aspnet-web-api/odata-routing-conventions

本文介绍的是Web API中使用的odata endpoints 的路由约定。

术语

URI:统一资源标识符(Uniform Resource Identifier,或URI)是一个用于标识某一互联网资源名称的字符串。 该种标识允许用户对网络中(一般指万维网)的资源通过特定的协议进行交互操作。URI包括URL和URN。

当Web API得到一个OData请求,它会自动映射到相应的Controller和Action名字。这个映射是基于HTTP方法和URI的。例如:GET /odata/Products(1) 映射到ProductsController.GetProduct.

本文Part 1介绍的是内置的OData路由约定。这些路由约定是专门为OData endpoints设计的,并且他们覆盖了默认的Web API路由系统(当调用MapODataRoute时覆盖将会发生)。

本文Part 2介绍的是如何添加自定义的OData路由约定。目前内置的OData路由约定并没有覆盖到所有的OData URIs,但是你可以扩展他们来处理额外的案例。

内置路由约定

首先下面有些方便理解OData Uri的准备知识:

URI 组成: 服务器根service root,资源路径resource path,查询选项Query options。

针对路由,最重要的部分是Resource Path。Resource Path被分成片段。例如上面的URI例子"Products(1)/Supplier"有三个片段:

  • Products: 代表名为Products的entity set。
  • 1:表示entity的主键,从entity set中短暂单个entity。
  • Supplier:属于导航属性(navigation property),选择相关联entity。

所以上面的例子查询结果:product 1的Supplier。

Controller Name

Controller Name 通常都是衍生于Resource Path根部的entity set。继续上面的例子“Products(1)/Supplier”:Web API查找名为ProductsController的controller。

Action Name

Action Name通常衍生于路径片段加上实体数据模型(EDM: Entity Data Model),如下表中所示。有时候,你有两种Action Name选择,如"Get"和“GetProduct”。

  • 查询entities


请求类型

URI例子

操作名字(Action)

Action例子

GET /entityset

/Products

GetEntitySet or Get

GetProducts

GET /entityset(key)

/Products(1)

GetEntityType or Get

GetProduct

GET /entityset(key)/cast

/Products(1)/Models.Book

GetEntityType or Get

GetBook


更多的资料查看:http://www.asp.net/web-api/overview/odata-support-in-aspnet-web-api/odata-v3/creating-an-odata-endpoint(OData V3)

  • 增、删、改entities



   请求类型

URI例子

操作名字(Action)

Action例子  

POST /entityset()

/Products

PostEntityType or Post

PostProduct

PUT /entityset(key)()

/Products(1)

PutEntityType or Put

PutProduct

PUT /entityset(key)/cast()

/Products(1)/Models.Book

PutEntityType or Put

PutBook

PATCH /entityset(key)()

/Products(1)

PatchEntityType or Patch

PatchProduct

PATCH /entityset(key)/cast()

/Products(1)/Models.Book

PatchEntityType or Patch

PatchBook

DELETE /entityset(key)()

/Products(1)

DeleteEntityType or Delete

DeleteProduct

DELETE /entityset(key)/cast()

/Products(1)/Models.Book

DeleteEntityType or Delete

DeleteBook

  • 查询导航属性( Navigation Property)


请求类型

URI例子

Action Name

Action例子

GET /entityset(key)/navigation

/Products(1)/Supplier

GetNavigationFromEntityType or GetNavigation

GetSupplierFromProduct

GET /entityset(key)/cast/navigation

/Products(1)/Models.Book/Author

GetNavigationFromEntityType or GetNavigation

GetAuthorFromBook

更多信息查看:Working with Entity Relations.

  • 增、删entities之间的联系(links)



请求类型

URI例子

Action Name

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


更多信息查看:Working with Entity Relations.

  • 属性properties(需要Web API 2)


请求类型

URI例子

Action Name

Action例子

GET /entityset(key)/property

/Products(1)/Name

GetPropertyFromEntityType or GetProperty

GetTitleFromBook

GET /entityset(key)/cast/property

/Products(1)/Models.Book/Author

GetPropertyFromEntityType or GetProperty

GetTitleFromBook


  • 操作Action



请求类型

URI例子

Action Name

Action例子

GET /entityset(key)/ action

/Products(1)/Rate

ActionNameOnEntityType or ActionName

RateOnProduct

GET /entityset(key)/cast/ action

/Products(1)/Models.Book/CheckOut

ActionNameOnEntityType or ActionName

CheckOutOnBook

更多信息查看:OData Actions.

  • 方法签名(Action Signatures)

这里有方法签名的几个规则:

  1. 如果路径中包括主键,action中必须有一个以主键为名的参数。
  2. 如果路径中包括导航属性中的主键(即关系entity的外键),action中必须有一个以外键为名的参数。
  3. [FromODataUri]参数来修饰主键外键参数。
  4. POSTPUT 请求需要有entity中的一个参数。
  5. PATCH请求需要Delta类型参数,其中T是entity类型

下面是每个内置Odatal路由约定的方法签名的例子:

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)}

自定义路由约定

可以通过实现IODataRoutingConvention接口来自定义路由约定,以添加其他可能OData URIs。该接口有两个方法:

string SelectController(ODataPath odataPath, HttpRequestMessage request);//返回Controller名字string SelectAction(ODataPath odataPath, HttpControllerContext controllerContext,     ILookup<string, HttpActionDescriptor> actionMap);//返回Action名字

两个方法,当该约定不应用于某个请求,则都是返回null。

直接给出一个例子吧:/odata/Products(1)/Suppliers(1)

以下是自定义约定来实现的query。

using System.Web.OData.Routing;using System.Web.OData.Routing.Conventions;using System.Linq;using System.Net.Http;using System.Web.Http.Controllers;using Microsoft.OData.Edm;namespace ODataRouting{    public class NavigationIndexRoutingConvention : EntitySetRoutingConvention    {        //返回Action名字        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;        }    }}


要点:

  1. 派生于EntitySetRoutingConvention,因为在这个类中SelectController方法适用于新的路由约定,不需要重写SelectController。
  2. 这个约定只适用于Get请求,而且只有当路径模式是:“~/entityset/key/navigation/key”。
  3. action名字是“Get{EntityType}”,其中{EntityType}是导航属性集合中类型。如:GetSupplier。你可以使用任何命名约定,只要Controller名字匹配上。
  4. action有key和relatekey两个参数。(ODataRouteConstants.

下一步将新约定加入到路由约定。(WebApiConfig.cs)

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);        }    }}

其他有用相关例子:

  • CompositeKeyRoutingConvention
  • CustomNavigationRoutingConvention
  • NonBindableActionRoutingConvention
  • ODataVersionRouteConstraint
  • source code 










    
    0 0