[水煮 ASP.NET Web API2 方法论](12-2)管理 OData 路由

问题

如何控制 OData 路由

 

解决方案

为了注册路由,可以使用  HttpConfigurationExtension 类中 MapODataServiceRoute 的扩展方法。对于单一路由这样做足以,其余的处理由实体数据模型来处理。

            config.MapODataServiceRoute("OData", "OData", builder.GetEdmModel());

从 ASP.NET Web API 2.2 开始支持 OData 直接声明路由,在 Action 上使用 ODataRouteAttribute。这和常规的属性路由一样,可以通过 ODataRoutePrefixAttribute 在 Controller 级别设置路由前缀.

1         [ODataRoute("Players")]
2 
3         public IQueryable<Player> GetAllPlayers()
4 
5         {
6 
7             // 忽略
8 
9         }

 

工作原理

OData 在 Web API 中路由是通过 ODataRoute 类实现的,其实,他是 HttpRoute 的一个子类。定制路由需要支持 ODataPathRouteConstraint,他是 OData 制定的,是IHttpRouteConstrain 的实现类,是为了确保所有的  OData 属性或 OData 路由约定在路由匹配后能够设置到 HttpRequestMessage 上。

ODataPath 类是用来包装转换 OData 资源路径,并以强类型的方式公开段(segment)。按约定,ASP.NET Web API 使用资源路径(URI 的一部分,例如,/Player),他是基于实体数据模型来映射到相应的 Controller。然后,动词依据动作选择在 Controller 中找到相应的 Action。此外,在 Web API 中 OData 可以使用自定义路由,这些路由都会影响动作选择。

 

小提示 更多关于 OData 路由约定的信息,请戳这里

https://www.asp.net/web-api/overview/OData-support-in-aspnet-web-api/OData-routing-conventions

 

我们可以通过我们自己的 IODataRoutingConvertion 重写 OData 路由行为、实现自定义 OData Controller、实现动作选择,如清单 12-3 所示

 

清单 12-3 IODataRoutingConvention 定义

 

 1     public interface IODataRoutingConvention
 2 
 3     {
 4 
 5         string SelectController(ODataPath ODataPath, HttpRequestMessage request);
 6 
 7         string SelectAction(ODataPath ODataPath, HttpControllerContext controllerContext,
 8 
 9         ILookup<string, HttpActionDescriptor> actionMap);
10 
11     }

 

当我们定义 OData 路由时,可以通过 MapODataServiceRoue 扩展方法来自定义路由约定;其中个的一个重载方法就是 使用 IODataRoutingConvention 的集合作为参数的方法。如果没有传值(例如,在使用 MapODataServiceRoute),那么,Web API 将在内部调用静态的 ODataRoutingConventions。CreateDefaultWithAttributeRouting 只使用默认的内建路由约定。

在 OData 中属性路由是 IDataRoutingConvention 的另一个版本-AttributeRoutingCinvention。他会查找所有 ODataRouteAttribute 的用法,使用 Dictionary<ODataPathTemplate,HttpActionDescriptot> 的形式建立适当的映射关系。如果进来的请求与当前 HTTP 请求的 ODataPath 匹配,那么,与此相关的 HttpActionDescriptor Controller 将会被选翻牌子来处理请求。

属性路由对于非标准路由来说是不错的选择,例如,使用非绑定的 OData 功能或 Action 的时候。尝试使用集中路由的方式进行路由,就需要自定义路由约定,然而,属性路由可以使用 ODataRouteAttribute 相关方法,用很简单的声明方式直接完成。

需要注意的是属性路由与常规路由不同,他是默认启用的。也就是说,除非我们重写默认的 IODataRoutingConventions,否则,Web API 会调用 ODataRoutingConvents。无论什么时候使用 MapODataServiceRoute 方法,CreateDefaultWithAttributeRouting 内部都会确保被 Web API OData 使用的 AttributeRoutingConvention 包含在约定的集合中。如清单 12-4 所示,摘录自 Web API 源码。

 

清单 12-4 ODataRoutingConvrention 类,确保 AttributeRoutingConvention 被包含。

 1     public static class ODataRoutingConventions
 2 
 3     {
 4 
 5         public static IList<IODataRoutingConvention> CreateDefaultWithAttributeRouting(
 6 
 7         HttpConfiguration configuration,
 8 
 9         IEdmModel model)
10 
11         {
12 
13             if (configuration == null)
14 
15             {
16 
17                 throw Error.ArgumentNull("configuration");
18 
19             }
20 
21             if (model == null)
22 
23             {
24 
25                 throw Error.ArgumentNull("model");
26 
27             }
28 
29             IList<IODataRoutingConvention> routingConventions = CreateDefault();
30 
31             AttributeRoutingConvention routingConvention = new AttributeRoutingConvention(model,
32 
33             configuration);
34 
35             routingConventions.Insert(0, routingConvention);
36 
37             return routingConventions;
38 
39         }
40 
41         public static IList<IODataRoutingConvention> CreateDefault()
42 
43         {
44 
45             return new List<IODataRoutingConvention>()
46 
47             {
48 
49                 new MetadataRoutingConvention(),
50 
51                 new EntitySetRoutingConvention(),
52 
53                 new SingletonRoutingConvention(),
54 
55                 new EntityRoutingConvention(),
56 
57                 new NavigationRoutingConvention(),
58 
59                 new PropertyRoutingConvention(),
60 
61                 new RefRoutingConvention(),
62 
63                 new ActionRoutingConvention(),
64 
65                 new FunctionRoutingConvention(),
66 
67                 new UnmappedRequestRoutingConvention()
68 
69             };
70 
71         }
72 
73     }

 

 

结果就是,在应用程序启动,不在需要调用任何其他的方法通知框架扫描所有的路由属性。事实上,只有这样,才能在最开始的地方获取属性路由并调用 MapODataServiceRoute.

 

代码演示

为了介绍 OData Web API 的集中路由,我们只需要在 HttpConfiguration 中调用 MapODataServiceRoute 和传路由前缀,以及我们的 IEdmModel。一个完整的启动类列子,使用简单的 OData 实体和集中路由,如清单 12-5 所示,使用 Player 实体。

 

清单 12-5. 声明一个基本的 OData 路由启动类

 1     public class Startup
 2 
 3     {
 4 
 5         public void Configuration(IAppBuilder builder)
 6 
 7         {
 8 
 9             var ODataBuilder = new ODataConventionModelBuilder();
10 
11             ODataBuilder.EntitySet<Player>("Players");
12 
13             var edm = ODataBuilder.GetEdmModel();
14 
15             var config = new HttpConfiguration();
16 
17             config.MapODataServiceRoute("Default OData", "OData", edm);
18 
19             builder.UseWebApi(config);
20 
21         }
22 
23     }
24 
25     public class Player
26 
27     {
28 
29         public int Id { get; set; }
30 
31         public string Name { get; set; }
32 
33         public string Team { get; set; }
34 
35     }

 

允许我们使用所有默认的内建路由约定,例如,

• myapi.com/OData/Players

• myapi.com/OData/Players(key)

• myapi.com/OData/Players(key)/{navigation property | property}

• myapi.com/OData/Players(key)/{function | action}

 

注意 默认的 Web API OData 路由约定使用了 key 的概念,不是 ID,所以我们 Controller 的 Action 应该接收一个叫做 Key 的参数。

 

清单 12-6 展示了 ODataController 使用两个 Action 方法。这两个方法都是通过属性路由的方式声明了 OData 路由。

 

清单 12-6. OData Controller 使用属性路由的例子

 1     [ODataRoutePrefix("Players")]
 2 
 3     public class PlayersController : ODataController
 4 
 5     {
 6 
 7         private readonly PlayersContext _players = new PlayersContext();
 8 
 9         [EnableQuery]
10 
11         [ODataRoute]
12 
13         public IQueryable<Player> GetAllPlayers()
14 
15         {
16 
17             return _players.AsQueryable();
18 
19         }
20 
21         [EnableQuery]
22 
23         [ODataRoute("({key})")]
24 
25         public SingleResult<Player> GetSinglePlayers(int key)
26 
27         {
28 
29             return SingleResult.Create(_players.Where(x => x.Id == key).AsQueryable());
30 
31         }
32 
33     }

 

posted @ 2016-11-21 08:45  水煮Code  阅读(1478)  评论(2编辑  收藏  举报