What – OData是什么?

OData - Open Data Protocol,是一个设计和使用RESTful API的标准。REST本身只是一个构建web服务的思想和理念,其没有规定一个统一的标准来限制开发人员该如何设计RESTful API。其实我们实际开发中的确也没有遵循某个统一的标准去设计WebAPI。因为大多数场景下,遵循一个统一的标准并不是必要的。但在某些场景下,有这样一个标准却能带来很大的好处。

OData的理想是, 无论哪个组织构建的RESTful API,只要其符合OData标准。其他组织就可以按照OData标准中定义的方式去使用这个API获取/修改资源。这个可以类比SQL标准之于RDBMS关系。无论什么关系型数据库,如果其声称支持SQL 标准,任何人就可以使用标准SQL查询语句来查询数据。

标准化的另一个好处:可以将Odata协议实现到一个通用的类库中,通过这个类库去创建和访问RESTful API可以减少开发人员的工作量。官网上有很多这样的组件。

Who - 谁发布了OData?

该标准由微软发起,前三个版本1.0、2.0、3.0都是微软开放标准。

When - 什么时候成为了工业标准?

第四个版本4.0于2014年3月17日在OASIS投票通过成为开放工业标准

Why – 为什么需要OData?

OData是一个协议,一个标准。所以这个问题等同于为什么我们需要协议。类比TCP协议就可以理解一般。假设你开发的组件必须要和某个第三方组件通信,如果第三方组件不支持TCP而只支持其内部开发的一个私有协议,你就肯定头大了,你必须在你的组件里单独为其实现这个私有协议。如果大家都支持TCP协议,不就省事了么。这就是标准协议的作用:协议和标准用于制定一个统一通用的规则。 我们只需要按照这个协议或标准生产组件,那么这个组件就可以方便的和其他组件集成/协作。而无须根据其他组件的私有标准定制化组件。

前面说到Rest只是一种设计Web服务的思想,不是一种标准化的协议。正由于缺乏标准化,从而导致各家公布的Restful API 统一通用方面的欠缺。OData就是为弥补这种欠缺而被提出来的标准协议。

下面全是延伸阅读可略过。

Web服务有两种实现方式,一是SOAP协议方式,二是REST方式。SOAP是一套完整的实现Web服务的解决方案。这里有必要先简单了解SOAP方式的Web服务,然后对比SOAP方式,我们会发现REST方式欠缺了什么。

SOAP方式的Web服务中的Web服务描述语言(WSDL)和简单对象访问协议(SOAP)一起构成了SOAP方式下的Web服务的结构单元。客户端通过WSDL可以了解Web服务公开了那些可以被执行的方法以及Web服务可以发送或接收的消息格式(解决了公布访问资源方法的问题)。客户端按照SOAP将调用位于远程系统上的服务所需信息序列化为消息(解决了如何调用远程方法的问题)。注意WSDL描述的服务以及SOAP消息都是符合统一标准的,都是机器可读的.

WSDL基于XML格式,用来描述Web服务。WSDL文档可以看成是客户端和服务器之间的一个协约。使用WSDL工具,你可以自动处理这个过程,几乎不用手工编写代码就能够让应用程序整合新的服务。因此WSDL是Web服务体系结构的基础,因为它提供了一个通用语言,用来描述服务和整合这些服务的平台。

SOAP本身提供了与Web服务交换信息的方法。SOAP是序列化调用位于远程系统上的服务所需信息的标准方法,这些信息可以使用一种远程系统能够读懂的格式通过网络发送到远程系统,而不必关心远程系统运行于何种平台或者使用何种语言编写。SOAP以XML格式提供了一个简单、轻量的用于在分散或分布环境中交换结构化和类型信息的机制。实际上它通过提供一个有标准组件的包模型和在模块中编码数据的机制,定义了一个简单的表示应用程序语义的机制。

对照SOAP方式的Web服务,REST中没有用于描述资源(服务)列表,资源元数据的类似于WSDL的东东。所以有人在2009年提出了一个标准WADL去描述REST方式的Web服务,但至今没有被标准化。个人认为使用WSDL/WADL去描述REST方式的Web服务太别扭,这是典型的RPC思路,而REST是一种把服务抽象为资源的架构思想。用描述RPC的WSDL去描述REST方式的Web服务并不合适。我们需要其他策略去代替WSDL实现“公布访问资源方法的问题”。

由于没有类似于SOAP的权威性协议作为规范,因此各个网站的REST实现都自有一套,也正是因为这种各自实现的情况,在性能和可用性上会大大高于SOAP发布的web service,但细节方面有太多没有约束的地方,其统一通用方面远远不及SOAP。

举个例子:假设A组织,B组织都实现了Restful API来通过工号查询人员信息,因为没有统一的规范。

A的API 可能是这样:http://A/api/person/001

B的API 可能是这样:http://A/api/person/id=001

第三方客户端在实现远程调用的时候就必须考虑这些API的差异,分别查看A,B的API文档。

如果有个权威性协议作为规范做指导,规定这个API应该实现成下面这样,那么第三方客户端也只需按照这个标准去调用远程API,而不用查看A,B的API文档:

http://A/api/person/{001}

解释了这么多,就是为了引出:OData是这样的一个设计和使用Restful API 的权威性协议. OData定义了一些标准规则(像一个接口定义一堆方法一样),实现Restful API时候,必须实现这些标准规则(就像实现一个接口必须实现其所有方法一样)。第三方就可以根据Odata协议定义的规则去访问Restful API。

Where –什么样的场景下可以考虑使用OData?

并不是说你创建的所有RESTful API都需要符合OData协议。只有在需要Open Data(开放数据给其他组织)时候,才有必要按照OData协议设计RESTful API。这里的Open Data是指开放数据给第三方使用,并且你并不知道谁是第三方。比如博客园的RSS,谁订阅了RSS,博客园是不清楚的。如果你的数据只被你自家公司的客户端使用, OData就是一个可选项,你完全有理由不按照OData规范去设计RESTful API。

How – 如何使用OData?

首先看一下C#客户端调用符合OData标准的WebApi是多么的方便(官网http://www.odata.org/上也有js的类库)。

第一步,通过Nuget安装OData Client for .Net包。

第二步,安装VS插件:OData v4 Client Code Generator。

第三步:假设存在一个可用的WebApi(后面介绍如何创建) - http://localhost:33189/Odata. 我们修改代码模板中的MetadataDocumentUri如下, 然后保存。T4会访问http://localhost:33189/Odata获得资源的元数据,然后根据元数据生成资源对应的C#类。T4可以怎么做是因为WebApi是按照OData的标准去公布资源列表和资源的元数据。

第四步:在我们的代码中就可以操作CLR对象来消费远程的webAPI了。体验到Odata标准的力量了吧。

接下来看一下C#服务端如何实现上面客户端需要调用的OData的WebAPI,有两种方式,有点细微的差别。

第一步:创建一个空的WebApi项目。

第二步: 通过Nuget引入EF6 和 WebApi 2.2 for OData v4.0. 如下图。

第三步:创建Entity和DbContext类,以及配置数据库连接。并通过enable migration完成数据库的创建,可在Configuration的seed的方法中,添加一些初始化的数据。

第四步:配置WebApiConfig如下

第五步:创建ProductsController

  1. using System;
  2. using System.Collections.Generic;
  3. using System.Data;
  4. using System.Data.Entity;
  5. using System.Data.Entity.Infrastructure;
  6. using System.Linq;
  7. using System.Net;
  8. using System.Net.Http;
  9. using System.Threading.Tasks;
  10. using System.Web.Http;
  11. using System.Web.Http.ModelBinding;
  12. using ODataAPI.Models;
  13. using System.Web.OData;
  14.  
  15. namespace ODataAPI.Controllers
  16. {
  17. /*
  18. To add a route for this controller, merge these statements into the Register method of the WebApiConfig class. Note that OData URLs are case sensitive.
  19.  
  20. using System.Web.Http.OData.Builder;
  21. using ODataAPI.Models;
  22. ODataConventionModelBuilder builder = new ODataConventionModelBuilder();
  23. builder.EntitySet<Product>("Products");
  24. config.Routes.MapODataRoute("odata", "odata", builder.GetEdmModel());
  25. */
  26. public class ProductsController : ODataController
  27. {
  28. private ODataAPIContext db = new ODataAPIContext();
  29.  
  30. // GET odata/Products
  31. //[Queryable]
  32. [EnableQuery]
  33. public IQueryable<Product> GetProducts()
  34. {
  35. return db.Products;
  36. }
  37.  
  38. // GET odata/Products(5)
  39. //[Queryable]
  40. [EnableQuery]
  41. public SingleResult<Product> GetProduct([FromODataUri] int key)
  42. {
  43. return SingleResult.Create(db.Products.Where(product => product.ID == key));
  44. }
  45.  
  46. // PUT odata/Products(5)
  47. public async Task<IHttpActionResult> Put([FromODataUri] int key, Product product)
  48. {
  49. if (!ModelState.IsValid)
  50. {
  51. return BadRequest(ModelState);
  52. }
  53.  
  54. if (key != product.ID)
  55. {
  56. return BadRequest();
  57. }
  58.  
  59. db.Entry(product).State = EntityState.Modified;
  60.  
  61. try
  62. {
  63. await db.SaveChangesAsync();
  64. }
  65. catch (DbUpdateConcurrencyException)
  66. {
  67. if (!ProductExists(key))
  68. {
  69. return NotFound();
  70. }
  71. else
  72. {
  73. throw;
  74. }
  75. }
  76.  
  77. return Updated(product);
  78. }
  79.  
  80. // POST odata/Products
  81. public async Task<IHttpActionResult> Post(Product product)
  82. {
  83. if (!ModelState.IsValid)
  84. {
  85. return BadRequest(ModelState);
  86. }
  87.  
  88. db.Products.Add(product);
  89. await db.SaveChangesAsync();
  90.  
  91. return Created(product);
  92. }
  93.  
  94. // PATCH odata/Products(5)
  95. [AcceptVerbs("PATCH", "MERGE")]
  96. public async Task<IHttpActionResult> Patch([FromODataUri] int key, Delta<Product> patch)
  97. {
  98. if (!ModelState.IsValid)
  99. {
  100. return BadRequest(ModelState);
  101. }
  102.  
  103. Product product = await db.Products.FindAsync(key);
  104. if (product == null)
  105. {
  106. return NotFound();
  107. }
  108.  
  109. patch.Patch(product);
  110.  
  111. try
  112. {
  113. await db.SaveChangesAsync();
  114. }
  115. catch (DbUpdateConcurrencyException)
  116. {
  117. if (!ProductExists(key))
  118. {
  119. return NotFound();
  120. }
  121. else
  122. {
  123. throw;
  124. }
  125. }
  126.  
  127. return Updated(product);
  128. }
  129.  
  130. // DELETE odata/Products(5)
  131. public async Task<IHttpActionResult> Delete([FromODataUri] int key)
  132. {
  133. Product product = await db.Products.FindAsync(key);
  134. if (product == null)
  135. {
  136. return NotFound();
  137. }
  138.  
  139. db.Products.Remove(product);
  140. await db.SaveChangesAsync();
  141.  
  142. return StatusCode(HttpStatusCode.NoContent);
  143. }
  144.  
  145. protected override void Dispose(bool disposing)
  146. {
  147. if (disposing)
  148. {
  149. db.Dispose();
  150. }
  151. base.Dispose(disposing);
  152. }
  153.  
  154. private bool ProductExists(int key)
  155. {
  156. return db.Products.Count(e => e.ID == key) > ;
  157. }
  158. }
  159. }

第六步:F5运行,接着客户端就可以调用了。可以通过访问http://localhost:#/OData/ 和 http://localhost:#/OData/$metadata 看看resource list 和元数据长什么样。

另外,我们可以通过VS的OData Controller模板来创建webAPIController(如下)。注意使用这种方式创建webAPIController时,不可以导入WebApi 2.2 for OData v4.0这个类库,否则会出现dll冲突。

OData的初步认识的更多相关文章

  1. [转]OData的初步认识 OData v4 Client Code Generator

    本文转自:http://www.cnblogs.com/1zhk/p/5356053.html What – OData是什么? OData - Open Data Protocol,是一个设计和使用 ...

  2. ABP源码分析三十八: ABP.Web.Api.OData

    如果对OData不熟悉的话可参考OData的初步认识一文以获取OData的一些初步知识. API.Odata 模块唯一用处就是提供了一个泛型版本的ODataController,实现了Controll ...

  3. 实战框架ABP

    abp及实战框架概述 接触abp也快一年了,有过大半年的abp项目开发经验,目前项目中所用的abp框架版本为0.10.3,最新的abp框架已经到了1.4,并且支持了asp.net core.关于abp ...

  4. 移动端之Android开发的几种方式的初步体验

    目前越来越多的移动端混合开发方式,下面列举的大多数我都略微的尝试过,就初步的认识写个简单的心得: 开发方式 开发环境 是否需要AndroidSDK 支持跨平台 开发语言&技能 MUI Win+ ...

  5. ABP框架 - OData 集成

    文档目录 本节内容: 简介 安装 安装Nuget包 设置模块依赖 配置你的实体 创建控制器 示例 获取实体列表 请求 响应 获取单个实体 请求 响应 获取单个实体及导航属性 请求 响应 查询 请求 响 ...

  6. CSharpGL(29)初步封装Texture和Framebuffer

    +BIT祝威+悄悄在此留下版了个权的信息说: CSharpGL(29)初步封装Texture和Framebuffer +BIT祝威+悄悄在此留下版了个权的信息说: Texture和Framebuffe ...

  7. Android自定义View初步

    经过上一篇的介绍,大家对于自定义View一定有了一定的认识,接下来我们就以实现一个图片下显示文字的自定义View来练习一下.废话不多说,下面进入我们的正题,首先看一下我们的思路,1.我们需要通过在va ...

  8. 初步认识Node 之Node为何物

    很多人即便是在使用了Node之后也不知道它到底是什么,阅读完本文你应该会有一个初步的.具体的概念了.    Node的目标 提供一种简单的构建可伸缩网络程序的方法.那么,什么是可伸缩网络程序呢?可伸缩 ...

  9. [入门级] 基于 visual studio 2010 mvc4 的图书管理系统开发初步 (二)

    [入门级] 基于 visual studio 2010 mvc4 的图书管理系统开发初步 (二) Date  周六 10 一月 2015 By 钟谢伟 Category website develop ...

随机推荐

  1. python/matplotlib库的安装

  2. asp.net 字符串替换、截取和从字符串中最后某个字符 开始截取

    有时候要在一段字符串里面把某些字符替换成其他字符,怎么办? 例如: string image=@"csks/news/user_top/qqqq/qqqq.jpg"; image ...

  3. VIew-CoordinatorLayout 笔记

    CoordinatorLayout 协调者:一般会是两个控件,一个Dependency一个child ,CoordinatorLayout的主要功能就是协调这两个控件,使child跟随Dependen ...

  4. Python 基础语法学习笔记

    以下运行结果均通过Python3.5版本实测! 1.列表转换为字典 a = ['a', 'b'] b = [1, 2] c = ['c','d'] print (dict([a,b,c])) 输出结果 ...

  5. 队列的JS实现

    队列和栈相似,都是对插入和删除操作的部位做了限制特殊的线性表.在队列中,只能从一头删除节点,这一头叫做队首:而另一端只能做插入操作,这一头叫做队尾.很容易理解,队列是一个"先进先出" ...

  6. 图论 - 寻找fly真迹

    一天fly正坐在课堂上发呆,突然,他注意到了桌面上的一个字符串S1S2S3S4...Sn,这个字符串只由字符"a","b"和"c"构成.刚好 ...

  7. OCIEnvNlsCreate 失败,返回代码为 -1,但错误消息文本不可用

    通过Navicat for Oracle能连接成功,增删改查正常,可一用到ADO.NET就报这个错误. 原来我一开始是用“管理员”方式安装的Client,后来用“InstantClient”方式重装就 ...

  8. js实现输入框数量加减【转】

    <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/ ...

  9. css实现一行文字居中,多行文字居左

    第一种方法: <style> *{margin:0;padding:0;} .box{width:500px;height:300px;border:1px solid #000;text ...

  10. 【Postgresql】数据库函数

    1.Postgresql查询前几条记录的SQL语句 select * from table where ...... LIMIT N  ; 2.SQL limit integer offset int ...