[水煮 ASP.NET Web API2 方法论](12-1)创建 OData

问题

怎样用在 Web API 中创建 OData 服务。

解决方案

对于我们来说,在 Web API 中使用 OData最简单的方式就是使用 ASP.NET 模板来创建Odata Controller。在 Controllers 文件夹上鼠标右键->添加->新建项。

显示一个如图 12-1 的对话框,在这里我们可以选择两个 “Web API 2 OData” 相关的模板。Vistual Studio 将会生成相关的 OData Controller,同时,从 NuGet 上下载 OData 需要的所有程序集。

图 12-1. 使用模板添加 OData
Controller

不过,这个模板仅仅对于 WEB Host (ASP.NET
Web API 托管在 ASP.NET Web 应用程序中)是可以用。对于 Web API 托管在其他地方,我们可以通过 NuGet 手动安装 OData Microsoft.AspNet.OData 来开启我们的 OData 开发之旅。

工作原理

OData 是一种通过 HTTP 公开丰富 API的标准化协议。OData 4.0 已经被 OASIS 国际开放标准联盟批准,也被认为是 Web 界的 ODBC。

Open Data
Protocol(OData )可以创建基于REST 的数据服务,可以是资源,使用 URL 和定义的数据模型,可以通过 Web 客户端使用简单的 HTTP 消息来发布和编辑。

OData 4.0

http://docs.oasis-open.org/odata/odata/v4.0/odata-v4.0-part1-protocol.html

小提示 OData 主页 www.odata.org 里面有所有感兴趣的资源,他可以帮助我们了解 Odato 协议。

ASP.NET WWB API 2.2 支持 OData 4.0(Microsoft.AspNet.OData NuGet 包),然而,之前的 Web API 支持的 OData 3.0。如果我们要是指定引用 Mircosoft.Aspnet.WebApi.OData NuGet 包,还是可以使用 OData 3.0。

OData Controller 应该继承自 ODataController 基类,而不是常规的ApiController。ASP.NET Web API 允许我们在一个项目中混合使用 OData 的Controller 和 传统的 Controller,所以,我们可以在提供 OData Api 的同时提供常规 Api。

Controller 继承 ODataController 是有框架进行不同配置的。被称为 ODataActionSelector 的 Odata IHttpAcionSelector 的实现类,是基于 Odata 路由的约定,以及一组特定的媒体类型格式化也是被默认替换的。所有的 OData 格式化程序都是 ODataMediaTypeFormatter 的变种,他可以处理 OData 指定的请求和相应格式,XML 和 JSON。

代码演示

清单 12-1 展示了一个完成的功能,而且很典型的 ODataController 的 CRUD。在这样的情况下,会通过 ASP.NET 的模板根据 Player 实体和 EF 数据上下文生成 Controller。

清单 12-1 典型的 ODataController

namespace BoiledCode.WebApi.Recipe.ODataDemo.Models
{
    public class Player
    {
        public int Id { get; set; }
        public string Name { get; set; }
        public string Team { get; set; }
    }
}

using System.Data.Entity.Infrastructure;
using System.Linq;
using System.Net;
using System.Web.Http;
using System.Web.OData;
using BoiledCode.WebApi.Recipe.ODataDemo.Models;
 
namespace BoiledCode.WebApi.Recipe.ODataDemo.Controllers
{
    /*
    The WebApiConfig class may require additional changes to add a route for this controller. Merge these statements into the Register method of the WebApiConfig class as applicable. Note that OData URLs are case sensitive.
 
    using System.Web.OData.Builder;
    using System.Web.OData.Extensions;
    using BoiledCode.WebApi.Recipe.ODataDemo.Models;
    ODataConventionModelBuilder builder = new ODataConventionModelBuilder();
    builder.EntitySet<Player>("Players");
    config.MapODataServiceRoute("odata", "odata", builder.GetEdmModel());
    */
 
    public class PlayersController : ODataController
    {
        private readonly ApplicationDbContext db = new ApplicationDbContext();
 
        // GET: odata/Players
        [EnableQuery]
        public IQueryable<Player> GetPlayers()
        {
            return db.Players;
        }
 
        // GET: odata/Players(5)
        [EnableQuery]
        public SingleResult<Player> GetPlayer([FromODataUri] int key)
        {
            return SingleResult.Create(db.Players.Where(player => player.Id == key));
        }
 
        // PUT: odata/Players(5)
        public IHttpActionResult Put([FromODataUri] int key, Delta<Player> patch)
        {
            Validate(patch.GetEntity());
 
            if (!ModelState.IsValid)
            {
                return BadRequest(ModelState);
            }
 
            var player = db.Players.Find(key);
            if (player == null)
            {
                return NotFound();
            }
 
            patch.Put(player);
 
            try
            {
                db.SaveChanges();
            }
            catch (DbUpdateConcurrencyException)
            {
                if (!PlayerExists(key))
                {
                    return NotFound();
                }
                throw;
            }
 
            return Updated(player);
        }
 
        // POST: odata/Players
        public IHttpActionResult Post(Player player)
        {
            if (!ModelState.IsValid)
            {
                return BadRequest(ModelState);
            }
 
            db.Players.Add(player);
            db.SaveChanges();
 
            return Created(player);
        }
 
        // PATCH: odata/Players(5)
        [AcceptVerbs("PATCH", "MERGE")]
        public IHttpActionResult Patch([FromODataUri] int key, Delta<Player> patch)
        {
            Validate(patch.GetEntity());
 
            if (!ModelState.IsValid)
            {
                return BadRequest(ModelState);
            }
 
            var player = db.Players.Find(key);
            if (player == null)
            {
                return NotFound();
            }
 
            patch.Patch(player);
 
            try
            {
                db.SaveChanges();
            }
            catch (DbUpdateConcurrencyException)
            {
                if (!PlayerExists(key))
                {
                    return NotFound();
                }
                throw;
            }
 
            return Updated(player);
        }
 
        // DELETE: odata/Players(5)
        public IHttpActionResult Delete([FromODataUri] int key)
        {
            var player = db.Players.Find(key);
            if (player == null)
            {
                return NotFound();
            }
 
            db.Players.Remove(player);
            db.SaveChanges();
 
            return StatusCode(HttpStatusCode.NoContent);
        }
 
        protected override void Dispose(bool disposing)
        {
            if (disposing)
            {
                db.Dispose();
            }
            base.Dispose(disposing);
        }
 
        private bool PlayerExists(int key)
        {
            return db.Players.Count(e => e.Id == key) > 0;
        }
    }
}

这个控制器和正常的 Controller 非常相似,只有几个地方是需要强调

  • OData 查询语法是通过
        EnableQueryAttribute 来启用的。我们将在 12-3 来继续讨论。
  • OData 查询语法不仅可以用在集合上也可以用在单个实体上,用在单个实体上的时候,只要实体使用  SingleResult<T> 就可以。关于这个我们也是在 12-3 来详细介绍。
  • 从 URI 绑定的时候,需要使用
        FromODataUriAttribute,而不是传统的 Web API FormUriAttribute。
  • OData Controller 一般是允许部分实体的更新。这个例子上,是通过 HTTP 的 PATCH 和 Delta<T> 来实现部分更新。Delta<T> 是一种特殊的类型,可以用于比较两个实体之间的差异,但是,他仅仅适用于
        ODataMediaTypeFormatters 类型。

很显然,控制器并非万能的。使用 OData 的最小要求就是为OData 创建一个实体数据模型(EDM)和 设置OData 路由。这些最终操作的都是 Web API HttpConfiguration 的实例。如清单 12-2 所示,我们会在下一次(12-2)来介绍 OData 路由。EDM 是用来为我们的服务定义 URI,以及提供语义描述(元数据)。

清单 12-1. 设置 EDM 和 OData 路由

        public void SettingUpEdmRoyte()
        {
            var config = new HttpConfiguration();
            //配置 Web API
            var builder = new ODataConventionModelBuilder();
            builder.EntitySet<Player>("Players");
            // 第一个参数:路由名称,第二个参数:OData 路由前缀
            // players 资源可以被 /odata/players 访问
            config.MapODataServiceRoute("odata", "odata", builder.GetEdmModel());
        }

这个 ODataConventionModelBuilder 类可以帮我们创建一个 EDM,我们不需要不必担心名称转换,导航属性,主键。如果我们需要自定义这些默认关系,那么,我们就需要使用它的基类 ODataModelBuilder,而不是 ODataConventionModelBuilder。

EntitySet方法添加实体并设置为 EDM 同时定义指定的 ODataController 来处理相应资源的 HTTP 请求,在我们的例子中就是 PlayersController。

时间: 2024-10-14 09:46:17

[水煮 ASP.NET Web API2 方法论](12-1)创建 OData的相关文章

[水煮 ASP.NET Web API2 方法论](12-3)OData 查询

问题 Web API 怎么支持通用的 OData 系统查询项,例如 $select 或 $filter. 解决方案 为了在 Web API 中启用查询项,我们需要在 Action 上使用 EnableQueryAttribute. 如果 Action 没有返回集合,而是返回单个对象的实例,调用端仍然可以使用 $expand 和 $select 两个查询语句,要达到这个目的,我们必须将返回对象包装在 SingleResult<T> 中.集合和单个对象实例作为返回值的例子如订单 12-7 所示 清

[水煮 ASP.NET Web API2 方法论](3-9)空气路由的设置

阅读导航 问题 解决方案 工作原理 代码演示 在此解释一下,空气路由,是本人臆想出来,觉着更能表达 IgnoreRoute 的意图,如果看着辣眼睛^^,请见谅. 问题 我们在之定义过集中式路由,集中式路由有一个特点就是短路,但是现在我们不想让某些路由工作(匹配并由路由引擎处理请求).那么我们应该怎么办呢? 解决方案 其实思路很简单,就是想把某些路由忽略了.ASP.NET WEB API 提供了一个叫做的 StopRoutingHandler 的处理器,简单说,他就是一个消息处理器,可以通过他来强

[水煮 ASP.NET Web API2 方法论](1-5)ASP.NET Web API Scaffolding(模板)

问题 我们想快速启动一个 ASP.NET Web API 解决方案. 解决方案 APS.NET 模板一开始就支持 ASP.NET Web API.使用模板往我们的项目中添加 Controller,在我们解决方案的 Controllers 文件夹上右键,选择"添加"->"Scaffolding". 即用模式,可以从下面选择一个: Web API2 Controller Web API2 Controller with      actions, using En

[水煮 ASP.NET Web API2 方法论](3-2)直接式路由/属性路由

问题 怎么样可以使用更贴近资源(Controller,Action)的方式定义路由. 解决方案 可以使用属性路由直接在资源级别声明路由.只要简单的在 Action 上使用属性路由 RouteAttribute,然后传一个相关路由模板就可以.属性路由与集中式路由在路由模板含义上基本是一样的,所有路由参数应该使用花括号,同时要与使用的 Action 相匹配.直接式路由支持默认路由,可选参数,约束.详细分析请往下走. 1 [Route("api/teams/{id}")] 2 public

[水煮 ASP.NET Web API2 方法论](1-4)从 MVC Controller 链接到 API Controller 以及反向链接

问题 想创建一个从 ASP.NET MVC controller 到 ASP.NET Web API controller 的直接链接,或者反向链接. 解决方案 可以使用 System.Web.Http.Routing.UrlHelp 的实例来创建一个指向 Controller的链接,来暴露ApiController(作为 Url 属性).着和在 RequestContext 上一样,会被附加到 HttpRequestMessage 实例.为了达到这个目的,我们需要调用链接方法或路由方法,然后传

[水煮 ASP.NET Web API2 方法论](1-8)添加 Session 状态

问题 ASP.NET Web API 构建 Web 应用程序时,要求使用 Session 在服务器存储一些用户特定的信息 解决方案 ASP.NET Web API 不支持 Session,因为 API 根本不依赖于System.Web.他想试图摆脱伪造 Session,非 HTTP这样的概念. 然而,如果我们 在 ASP.NET 运行时中运行 ASP.NET Web API,还想启用 Session.我们可以通过两种方式来做: 全局:应用于整个 API 局部:应用于指定路由 启用全局方式,我们需

[水煮 ASP.NET Web API2 方法论](1-6)Model Validation

问题 想要 ASP.NET Web API 执行模型验证,同时可以和 ASP.NET MVC 共享一些验证逻辑. 解决方案 ASP.NET Web API 与 ASP.NET MVC 支持一样的验证机制,都是通过System.ComponentModel.DataAnnoataions 的属性验证.使用框架提供的相关验证属性,已足够来用来验证模型. 想要更细粒度的验证,我们可以选择在我们的模型中实现 IValudateObject(来自于System.ComponentModel.DataAnn

[水煮 ASP.NET Web API2 方法论](1-7)CSRF-Cross-Site Request Forgery

问题 通过 CSRF(Cross-Site Request Forgery)防护,保护从 MVC 页面提交到ASP.NET Web API 的数据. 解决方案 ASP.NET 已经加入了 CSRF 防护功能,只要通过 System.web.Helpers.AntiForgery 类(System.Web.WebPages 的一部分)就可以. 他会生成两个 Token: Cookie Token 基于字符串的 Token 基于字符串的 Token 是可以嵌入到表单或者请求头(使用 Ajax 的情况

[水煮 ASP.NET Web API2 方法论](3-5)路由约束

问题 怎么样限制路由中参数的值. 解决方案 ASP.NET WEB API 允许我们通过 IHttpRouteConstraint 接口设置路由约束.集中式路由和直接式路由都可以使用 IHttpRouteConstraint. 框架提供了 18 个接口,他提供了大部分类型的约束,例如,路由参数长度相关的约束,可以确保值都在定义范围内,或者限制数据类型.当然也可以通过实现接口 IHttpRouteConstraint 来自定义约束逻辑. 工作原理 IHttpRoutConstraint 是一个 H

[水煮 ASP.NET Web API2 方法论](3-4)设置路由可选项

问题 怎么样创建一个路由,不管客户端传不传这个参数,都可以被成功匹配. 解决方案 ASP.NET WEB API 的集中式路由和属性路由都支持路由声明可选参数. 在用集中式路由中可以通过 RouteParameter.Optional 指定一个可选参数,RouteParameter.Optional 等同于MVC 中的 UrlParameter.Option. 属性路由,通过在可选参数后面添加一个问好作为后缀.同时,必须为其指定默认值 工作原理 从根本上看,ASP.NET WEB API 支持的