1. 简介
在本文中,我们将探讨 OData(Open Data Protocol),这是一个用于通过 RESTful API 访问数据集的标准协议。它提供了一种标准化的方式来查询、操作和导航数据,非常适合构建统一的数据服务接口。
2. 什么是 OData?
OData 是一个由 OASIS 和 ISO/IEC 标准化的协议,用于通过 RESTful API 访问数据。它允许客户端通过标准的 HTTP 方法发现并操作数据集。
比如,我们可以使用 curl
快速访问一个公开的 OData 服务:
curl -s https://services.odata.org/V2/Northwind/Northwind.svc/Regions
<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<feed xml:base="https://services.odata.org/V2/Northwind/Northwind.svc/"
xmlns:d="http://schemas.microsoft.com/ado/2007/08/dataservices"
xmlns:m="http://schemas.microsoft.com/ado/2007/08/dataservices/metadata"
xmlns="http://www.w3.org/2005/Atom">
<title type="text">Regions</title>
<id>https://services.odata.org/V2/Northwind/Northwind.svc/Regions</id>
... 其他 XML 内容省略
当前最新版本是 OData 4.01。虽然 OData V4 在 2014 年就已成为 OASIS 标准,但它的历史可以追溯到微软的 Astoria 项目,后来改名为 ADO.NET Data Services。
使用 OData 有很多优势:
✅ 允许使用 Excel 等工具直接访问数据
✅ 丰富的 REST 客户端支持
✅ 支持安全机制、日志、监控等 HTTP 特性
正因为如此,很多政府机构在构建公共数据服务时都选择了 OData,例如 OData 公共服务目录。
3. OData 核心概念
OData 的核心是 Entity Data Model(EDM),它描述了服务提供者所暴露的数据结构。EDM 通过 $metadata
接口提供元数据文档,其中包含:
- 实体类型(Entity Type)及其属性和主键(如 Person、Customer、Order)
- 实体之间的关系
- 复杂类型(如嵌入在 Customer 中的 Address 类型)
- 实体集合(Entity Set),用于聚合特定类型的实体
3.1. EntityType
元素
定义实体的属性和主键,例如:
<EntityType Name="CarMaker">
<Key>
<PropertyRef Name="Id"/>
</Key>
<Property Name="Id" Type="Edm.Int64" Nullable="false"/>
<Property Name="Name" Type="Edm.String" Nullable="true" MaxLength="255"/>
<NavigationProperty Name="CarModelDetails" Relationship="default.CarModel_CarMaker_Many_One0" FromRole="CarMaker" ToRole="CarModel"/>
</EntityType>
这个 CarMaker
实体有两个属性:Id
和 Name
,还有一个导航属性 CarModelDetails
,表示与 CarModel
的关联关系。
3.2. Association
元素
定义两个实体之间的关联关系,包括多重性(如一对多)和引用完整性约束:
<Association Name="CarModel_CarMaker_Many_One0">
<End Type="default.CarModel" Multiplicity="*" Role="CarModel"/>
<End Type="default.CarMaker" Multiplicity="1" Role="CarMaker"/>
<ReferentialConstraint>
<Principal Role="CarMaker">
<PropertyRef Name="Id"/>
</Principal>
<Dependent Role="CarModel">
<PropertyRef Name="Maker"/>
</Dependent>
</ReferentialConstraint>
</Association>
该示例定义了 CarModel
与 CarMaker
的一对多关系。
3.3. EntitySet
元素
代表某一实体类型的集合,类似于数据库中的“视图”:
<EntityContainer Name="defaultContainer" m:IsDefaultEntityContainer="true">
<EntitySet Name="CarModels" EntityType="default.CarModel"/>
<EntitySet Name="CarMakers" EntityType="default.CarMaker"/>
</EntityContainer>
每个 EntitySet
对应一个实体类型,我们可以定义多个 EntitySet
来展示不同的数据子集。
4. OData URL 与方法
OData 使用标准 HTTP 方法来操作数据:
方法 | 作用 |
---|---|
GET | 获取一个或多个实体 |
POST | 添加新实体 |
PUT | 替换整个实体 |
PATCH | 替换部分属性 |
DELETE | 删除实体 |
URL 示例:
http://example.org/odata/CarMakers
这个 URL 的结构如下:
http://example.org/odata/
:服务根路径(Service Root)CarMakers
:对应服务元数据中的某个EntitySet
访问该 URL 将返回一个 XML 文档,包含所有 CarMakers
实体的列表。
<feed>
<entry>
<id>http://example.org/odata/CarMakers(1L)</id>
<title type="text">CarMakers</title>
<content type="application/xml">
<m:properties>
<d:Id>1</d:Id>
<d:Name>Special Motors</d:Name>
</m:properties>
</content>
</entry>
</feed>
其中:
id
:指向该实体的链接title
、updated
:元数据信息link
:编辑或关联实体的链接content
:实体的属性值
注意:CarMaker(1L)
中的 L
表示这是一个 long
类型的主键,可以省略。
5. 查询选项(Query Options)
OData 提供了多种查询参数,用于控制返回数据的格式和内容。
5.1. $top
和 $skip
用于分页查询:
.../CarMakers?$top=10&$skip=10
$top=10
:返回前 10 条记录$skip=10
:跳过前 10 条记录
获取集合总数:
.../CarMakers/$count
⚠️ 注意:OData V2 和 V4 的 $count
使用方式不同:
- V2:
.../CarMakers/$count
- V4:
.../CarMakers?$count=true
5.2. $filter
用于过滤数据,支持逻辑表达式和函数:
.../CarMakers?$filter=startswith(Name,'B')
复合条件查询:
.../CarModels?$filter=Year eq 2008 and CarMakerDetails/Name eq 'BMW'
5.3. $expand
默认情况下,OData 不会返回关联实体的数据。使用 $expand
可以一并获取:
.../CarModels(1L)?$expand=CarMakerDetails
返回结果中将包含 CarMakerDetails
的完整数据。
5.4. $select
用于指定返回的字段,避免返回冗余数据:
.../CarModels(1L)?$select=Name,Sku
返回内容仅包含 Name
和 Sku
字段。
5.5. $orderBy
控制返回数据的排序方式,支持多个字段和排序方向:
.../CarModels?$orderBy=Name asc,Sku desc
⚠️ 注意:排序方向 asc
和 desc
必须使用小写。
5.6. $format
指定返回数据的格式(优先于 Accept
请求头):
.../CarModels?$format=json
服务将返回 JSON 格式数据。如果未指定,将根据 Accept
头决定返回格式。
6. 总结
本文介绍了 OData 协议的基本概念和使用方式,包括 EDM、HTTP 方法、URL 结构以及常用查询参数。OData 提供了一种标准化的数据访问方式,适合构建统一的数据服务接口。
后续文章将介绍如何使用 Olingo 库实现 OData 服务。