1. Introduction
1.绪论
In this tutorial, we’ll explore OData, a standard protocol that allows easy access to data sets using a RESTFul API.
在本教程中,我们将探讨OData,这是一个标准协议,允许使用RESTFul API轻松访问数据集。。
2. What Is OData?
2.什么是OData?
OData is an OASIS and ISO/IEC Standard for accessing data using a RESTful API. As such, it allows a consumer to discover and navigate through data sets using standard HTTP calls.
OData是一个OASIS和ISO/IEC标准,用于使用RESTful API访问数据。因此,它允许消费者使用标准的HTTP调用来发现和浏览数据集。
For instance, we can access one of the publicly available OData services with a simple curl one-liner:
例如,我们可以通过一个简单的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>
... rest of xml response omitted
As of this writing, the OData protocol is at its 4th version – 4.01 to be more precise. OData V4 reached the OASIS standard level in 2014, but it has a longer history. We can trace its roots to a Microsoft project called Astoria, which was renamed to ADO.Net Data Services in 2007. The original blog entry announcing this project is still available at Microsoft’s OData blog.
截至目前,OData协议已经到了第四个版本–更准确地说,是4.01。OData V4在2014年达到了OASIS标准水平,但它的历史更长。我们可以追溯到一个名为Astoria的微软项目,该项目在2007年被重新命名为ADO.Net数据服务。宣布这个项目的原始博客条目仍可在Microsoft的OData博客上找到。
Having a standards-based protocol to access data set brings some benefits over standard APIs such as JDBC or ODBC. As an end-user level consumer, we can use popular tools such as Excel to retrieve data from any compatible provider. Programming is also facilitated by a large number of available REST client libraries.
拥有一个基于标准的协议来访问数据集,比标准API(如JDBC或ODBC)带来了一些好处。作为终端用户级别的消费者,我们可以使用Excel等流行工具从任何兼容的提供者那里检索数据。大量可用的REST客户端库也为编程提供了便利。
As providers, adopting OData also has benefits: once we’ve created a compatible service, we can focus on providing valuable data sets, that end-users can consume using the tools of their choice. Since it is an HTTP-based protocol, we can also leverage aspects such as security mechanisms, monitoring, and logging.
作为供应商,采用OData也有好处:一旦我们创建了一个兼容的服务,我们就可以专注于提供有价值的数据集,最终用户可以使用他们选择的工具来消费。由于它是一个基于HTTP的协议,我们还可以利用安全机制、监控和日志等方面。
Those characteristics made OData a popular choice by government agencies when implementing public data services, as we can check by taking a look at this directory.
这些特点使OData成为政府机构在实施公共数据服务时的热门选择,我们可以通过看一看这个目录来检查。
3. OData Concepts
3.OData的概念
At the core of the OData protocol is the concept of an Entity Data Model – or EDM for short. The EDM describes the data exposed by an OData provider through a metadata document containing a number of meta-entities:
OData协议的核心是实体数据模型的概念,简称EDM。EDM通过一个包含若干元实体的元数据文档来描述OData提供者所暴露的数据。
- Entity type and its properties (e.g. Person, Customer, Order, etc) and keys
- Relationships between entities
- Complex types used to describe structured types embedded into entities (say, an address type which is part of a Customer type)
- Entity Sets, which aggregate entities of a given type
The spec mandates that this metadata document must be available at the standard location $metadata at the root URL used to access the service. For instance, if we have an OData service available at http://example.org/odata.svc/, then its metadata document will be available at http://example.org/odata.svc/$metadata.
该规范规定,该元数据文档必须在用于访问服务的根URL的标准位置$metadata中提供。例如,如果我们在http://example.org/odata.svc/上有一个OData服务,那么它的元数据文档将在http://example.org/odata.svc/$metadata上提供。
The returned document contains a bunch of XML describing the schemas supported by this server:
返回的文档包含一堆描述该服务器所支持的模式的XML。
<?xml version="1.0"?>
<edmx:Edmx
xmlns:edmx="http://schemas.microsoft.com/ado/2007/06/edmx"
Version="1.0">
<edmx:DataServices
xmlns:m="http://schemas.microsoft.com/ado/2007/08/dataservices/metadata"
m:DataServiceVersion="1.0">
... schema elements omitted
</edmx:DataServices>
</edmx:Edmx>
Let’s tear down this document into its main sections.
让我们把这份文件拆成主要部分。
The top-level element, <edmx:Edmx> can have only one child, the <edmx:DataServices> element. The important thing to notice here is the namespace URI since it allows us to identify which OData version the server uses. In this case, the namespace indicates that we have an OData V2 server, which uses Microsoft’s identifiers.
顶层元素<edmx:Edmx>只能有一个子元素,即<edmx:DataServices>元素。这里要注意的是命名空间URI,因为它允许我们识别服务器使用的OData版本。在这种情况下,该命名空间表明我们有一个OData V2服务器,它使用微软的标识符。
A DataServices element can have one or more Schema elements, each describing an available dataset. Since a full description of the available elements in a Schema is beyond the scope of this article, we’ll focus on the most important ones: EntityTypes, Associations, and EntitySets.
一个DataServices元素可以有一个或多个Schema元素,每个元素描述了一个可用的数据集。由于全面描述Schema中的可用元素超出了本文的范围,我们将集中讨论最重要的元素。EntityTypes、Associations、和EntitySets。
3.1. EntityType Element
3.1.EntityType元素
This element defines the available properties of a given entity, including its primary key. It may also contain information about relationships with other schema types and, by looking at an example – a CarMaker – we’ll be able to see that it is not very different from descriptions found in other ORM technologies, such as JPA:
这个元素定义了一个给定实体的可用属性,包括它的主键。它还可能包含与其他模式类型的关系信息,通过看一个例子–一个CarMaker–,我们就能看到它与其他ORM技术(如JPA)中的描述没有很大区别。
<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>
Here, our CarMaker has only two properties – Id and Name – and an association to another EntityType. The Key sub-element defines the entity’s primary key to be its Id property, and each Property element contains data about an entity’s property such as its name, type or nullability.
在这里,我们的CarMaker只有两个属性–Id和Name,以及与另一个EntityType的关联。Key s子元素定义了实体的主键是它的Id属性,而每个Property元素都包含了关于实体属性的数据,如它的名称、类型或无效性。
A NavigationProperty is a special kind of property that describes an “access point” to a related entity.
NavigationProperty是一种特殊的属性,它描述了一个通往相关实体的 “访问点”。
3.2. Association Element
3.2 协会元素
An Association element describes an association between two entities, which includes the multiplicity on each end and optionally a referential integrity constraint:
一个关联元素描述了两个实体之间的关联,它包括每一端的多重性和可选的参考完整性约束。
<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>
Here, the Association element defines a one-to-many relationship between a CarModel and CarMaker entities, where the former acts as the dependent party.
这里,Association元素定义了CarModel和CarMaker实体之间的一对多关系,其中前者作为从属方。
3.3. EntitySet Element
3.3.EntitySet Element
The final schema concept we’ll explore is the EntitySet element, which represents a collection of entities of a given type. While it’s easy to think them as analogous to a table – and in many cases, they’re just that – a better analogy is that of a view. The reason for that is that we can have multiple EntitySet elements for the same EntityType, each representing a different subset of the available data.
我们要探讨的最后一个模式概念是EntitySet元素,它代表了一个特定类型的实体集合。虽然我们很容易把它们看作是表的类似物–在许多情况下,它们就是这样–但更好的比喻是视图。原因是,我们可以为同一个EntityType元素设置多个EntitySet元素,每个元素代表可用数据的不同子集。
The EntityContainer element, which is a top-level schema element, groups all available EntitySets:
EntityContainer元素是一个顶级的模式元素,它将所有可用的EntitySets分组。
<EntityContainer Name="defaultContainer"
m:IsDefaultEntityContainer="true">
<EntitySet Name="CarModels"
EntityType="default.CarModel"/>
<EntitySet Name="CarMakers"
EntityType="default.CarMaker"/>
</EntityContainer>
In our simple example, we have just two EntitySets, but we could also add additional views, such as ForeignCarMakers or HistoricCarMakers.
在我们的简单例子中,我们只有两个EntitySets,但是我们也可以添加额外的视图,例如ForeignCarMakers或HistoricCarMakers。
4. OData URLs and Methods
4.OData URLs和方法
In order to access data exposed by an OData service, we use the regular HTTP verbs:
为了访问OData服务所暴露的数据,我们使用常规的HTTP动词。
- GET returns one or more entities
- POST adds a new entity to an existing Entity Set
- PUT replaces a given entity
- PATCH replaces specific properties of a given entity
- DELETE removes a given entity
All those operations require a resource path to act upon. The resource path may define an entity set, an entity or even a property within an entity.
所有这些操作都需要一个资源路径来进行操作。资源路径可以定义一个实体集、一个实体,甚至是一个实体中的一个属性。
Let’s take a look on an example URL used to access our previous OData service:
让我们来看看一个用于访问我们之前的OData服务的URL例子。
http://example.org/odata/CarMakers
The first part of this URL, starting with the protocol up to the odata/ path segment, is known as the service root URL and is the same for all resource paths of this service. Since the service root is always the same, we’ll replace it in the following URL samples by an ellipsis (“…”).
这个URL的第一部分,从协议开始一直到odata/路径段,被称为service root URL,对这个服务的所有资源路径都是一样的。 由于服务根总是相同的,我们将在下面的URL样本中用省略号(”…”)替换它。
CarMakers, in this case, refers to one of the declared EntitySets in the service metadata. We can use a regular browser to access this URL, which should then return a document containing all existing entities of this type:
CarMakers,在这种情况下,指的是服务元数据中声明的EntitySets之一。我们可以使用一个普通的浏览器来访问这个URL,然后它应该返回一个包含这个类型的所有现有实体的文档。
<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom"
xmlns:m="http://schemas.microsoft.com/ado/2007/08/dataservices/metadata"
xmlns:d="http://schemas.microsoft.com/ado/2007/08/dataservices"
xml:base="http://localhost:8080/odata/">
<id>http://localhost:8080/odata/CarMakers</id>
<title type="text">CarMakers</title>
<updated>2019-04-06T17:51:33.588-03:00</updated>
<author>
<name/>
</author>
<link href="CarMakers" rel="self" title="CarMakers"/>
<entry>
<id>http://localhost:8080/odata/CarMakers(1L)</id>
<title type="text">CarMakers</title>
<updated>2019-04-06T17:51:33.589-03:00</updated>
<category term="default.CarMaker"
scheme="http://schemas.microsoft.com/ado/2007/08/dataservices/scheme"/>
<link href="CarMakers(1L)" rel="edit" title="CarMaker"/>
<link href="CarMakers(1L)/CarModelDetails"
rel="http://schemas.microsoft.com/ado/2007/08/dataservices/related/CarModelDetails"
title="CarModelDetails"
type="application/atom+xml;type=feed"/>
<content type="application/xml">
<m:properties>
<d:Id>1</d:Id>
<d:Name>Special Motors</d:Name>
</m:properties>
</content>
</entry>
... other entries omitted
</feed>
The returned document contains an entry element for each CarMaker instance.
返回的文档为每个CarMaker实例包含一个entry元素。
Let’s take a closer look at what information we have available to us:
让我们仔细看看我们有哪些信息可以利用。
- id: a link to this specific entity
- title/author/updated: metadata about this entry
- link elements: Links used to point to a resource used to edit the entity (rel=”edit”) or to related entities. In this case, we have a link that takes us to the set of CarModel entities associated with this particular CarMaker.
- content: property values of CarModel entity
An important point to notice here is the use of the key-value pair to identify a particular entity within an entity set. In our example, the key is numeric so a resource path like CarMaker(1L) refers to the entity with a primary key value equal to 1 – the “L” here just denotes a long value and could be omitted.
这里需要注意的一点是使用键值对来识别实体集中的特定实体。在我们的例子中,键是数字,所以像CarMaker(1L)这样的资源路径是指主键值等于1的实体–这里的”L“只是表示一个长值,可以省略。
5. Query Options
5.查询选项
We can pass query options to a resource URL in order to modify a number of aspects of the returned data, such as to limit the size of the returned set or its ordering. The OData spec defines a rich set of options, but here we’ll focus on the most common ones.
我们可以向资源URL传递查询选项,以修改返回数据的许多方面,比如限制返回数据集的大小或排序。OData规范定义了一套丰富的选项,但在这里我们将重点讨论最常见的选项。
As a general rule, query options can be combined with each other, thus allowing clients to easily implement common functionalities such as paging, filtering and ordering result lists.
一般来说,查询选项可以相互组合,从而使客户可以轻松实现常见的功能,如分页、过滤和排序结果列表。
5.1. $top and $skip
5.1.$top和$skip
We can navigate through a large dataset using the $top an $skip query options:
我们可以使用$top和$skip查询选项在大型数据集中进行导航。
.../CarMakers?$top=10&$skip=10
$top tells the service that we want only the first 10 records of the CarMakers entity set. A $skip, which is applied before the $top, tells the server to skip the first 10 records.
$top告诉服务,我们只想要CarMakers实体集的前10条记录。一个$skip,应用在$top之前,告诉服务器要跳过前10条记录。
It’s usually useful to know the size of a given Entity Set and, for this purpose, we can use the $count sub-resource:
通常,了解某个实体集的大小是很有用的,为此,我们可以使用$count子资源。
.../CarMakers/$count
This resource produces a text/plain document containing the size of the corresponding set. Here, we must pay attention to the specific OData version supported by a provider. While OData V2 supports $count as a sub-resource from a collection, V4 allows it to be used as a query parameter. In this case, $count is a Boolean, so we need to change the URL accordingly:
该资源会产生一个text/plain文件,其中包含了相应集合的大小。在这里,我们必须注意提供者所支持的特定OData版本。虽然OData V2支持$count作为集合的子资源,但V4允许它作为查询参数使用。在这种情况下,$count是一个布尔值,所以我们需要相应地改变URL。
.../CarMakers?$count=true
5.2. $filter
5.2. $filter
We use the $filter query option to limit the returned entities from a given Entity Set to those matching given criteria. The value for the $filter is a logical expression that supports basic operators, grouping and a number of useful functions. For instance, let’s build a query that returns all CarMaker instances where its Name attribute starts with the letter ‘B’:
我们使用$filter查询选项来将返回的实体从给定的Entity Set限制在符合给定条件的实体。$filter的值是一个逻辑表达式,支持基本运算符、分组和一些有用的功能。例如,让我们建立一个查询,返回所有CarMaker实例,其中其Name属性以字母 “B “开头。
.../CarMakers?$filter=startswith(Name,'B')
Now, let’s combine a few logical operators to search for CarModels of a particular Year and Maker:
现在,让我们结合一些逻辑运算符来搜索特定年份和制造商的汽车模型。
.../CarModels?$filter=Year eq 2008 and CarMakerDetails/Name eq 'BWM'
Here, we’ve used the equality operator eq to specify values for the properties. We can also see how to use properties from a related entity in the expression.
这里,我们使用了平等操作符eq来指定属性的值。我们还可以看到如何在表达式中使用相关实体的属性。
5.3. $expand
5.3.$expand
By default, an OData query does not return data for related entities, which is usually OK. We can use the $expand query option to request that data from a given related entity be included inline with the main content.
默认情况下,OData查询不会返回相关实体的数据,这通常是可以的。我们可以使用$expand查询选项来请求将给定的相关实体的数据与主要内容并列包括。
Using our sample domain, let’s build an URL that returns data from a given model and its maker, thus avoiding an additional round-trip to the server:
使用我们的示例域,让我们建立一个URL,从一个给定的模型和其制造者那里返回数据,从而避免了到服务器的额外往返。
.../CarModels(1L)?$expand=CarMakerDetails
The returned document now includes the CarMaker data as part of the related entity:
现在返回的文档包括CarMaker数据,作为相关实体的一部分。
<?xml version="1.0" encoding="utf-8"?>
<entry xmlns="http://www.w3.org/2005/Atom"
xmlns:m="http://schemas.microsoft.com/ado/2007/08/dataservices/metadata"
xmlns:d="http://schemas.microsoft.com/ado/2007/08/dataservices"
xml:base="http://localhost:8080/odata/">
<id>http://example.org/odata/CarModels(1L)</id>
<title type="text">CarModels</title>
<updated>2019-04-07T11:33:38.467-03:00</updated>
<category term="default.CarModel"
scheme="http://schemas.microsoft.com/ado/2007/08/dataservices/scheme"/>
<link href="CarModels(1L)" rel="edit" title="CarModel"/>
<link href="CarModels(1L)/CarMakerDetails"
rel="http://schemas.microsoft.com/ado/2007/08/dataservices/related/CarMakerDetails"
title="CarMakerDetails"
type="application/atom+xml;type=entry">
<m:inline>
<entry xml:base="http://localhost:8080/odata/">
<id>http://example.org/odata/CarMakers(1L)</id>
<title type="text">CarMakers</title>
<updated>2019-04-07T11:33:38.492-03:00</updated>
<category term="default.CarMaker"
scheme="http://schemas.microsoft.com/ado/2007/08/dataservices/scheme"/>
<link href="CarMakers(1L)" rel="edit" title="CarMaker"/>
<link href="CarMakers(1L)/CarModelDetails"
rel="http://schemas.microsoft.com/ado/2007/08/dataservices/related/CarModelDetails"
title="CarModelDetails"
type="application/atom+xml;type=feed"/>
<content type="application/xml">
<m:properties>
<d:Id>1</d:Id>
<d:Name>Special Motors</d:Name>
</m:properties>
</content>
</entry>
</m:inline>
</link>
<content type="application/xml">
<m:properties>
<d:Id>1</d:Id>
<d:Maker>1</d:Maker>
<d:Name>Muze</d:Name>
<d:Sku>SM001</d:Sku>
<d:Year>2018</d:Year>
</m:properties>
</content>
</entry>
5.4. $select
5.4.$select
We use the $select query option to inform the OData service that it should only return the values for the given properties. This is useful in scenarios where our entities have a large number of properties, but we’re only interested in some of them.
我们使用$select查询选项来通知OData服务,它应该只返回指定属性的值。这在我们的实体有大量的属性,但我们只对其中的一部分感兴趣的情况下很有用。
Let’s use this option in a query that returns only the Name and Sku properties:
让我们在一个只返回Name和Sku属性的查询中使用这个选项。
.../CarModels(1L)?$select=Name,Sku
The resulting document now has only the requested properties:
现在产生的文件只具有所要求的属性。
... xml omitted
<content type="application/xml">
<m:properties>
<d:Name>Muze</d:Name>
<d:Sku>SM001</d:Sku>
</m:properties>
</content>
... xml omitted
We can also see that even related entities were omitted. In order to include them, we’d need to include the name of the relation in the $select option.
我们还可以看到,甚至相关的实体也被省略了。为了包括它们,我们需要在$select选项中包括关系的名称。
5.5. $orderBy
5.5.$orderBy
The $orderBy option works pretty much as its SQL counterpart. We use it to specify the order in which we want the server to return a given set of entities. In its simpler form, its value is just a list of property names from the selected entity, optionally informing the order direction:
$orderBy选项的工作原理与它的SQL对应项差不多。我们用它来指定我们希望服务器返回一组给定的实体的顺序。在更简单的形式下,它的值只是一个来自所选实体的属性名称的列表,可以选择告知顺序方向。
.../CarModels?$orderBy=Name asc,Sku desc
This query will result in a list of CarModels ordered by their names and SKUs, in ascending and descending directions, respectively.
该查询将产生一个汽车模型的列表,按其名称和SKU分别以升序和降序排列。
An important detail here is the case used with the direction part of a given property: while the spec mandates that server must support any combination of upper- and lower-case letters for the keywords asc and desc, it also mandates that client use only lowercase.
这里的一个重要细节是给定属性的方向部分所使用的大小写:虽然规范规定服务器必须支持关键词asc 和desc的大写和小写字母的任何组合,但它也规定客户端只使用小写。
5.6. $format
5.6.$format
This option defines the data representation format that the server should use, which takes precedence over any HTTP content-negotiation header, such as Accept. Its value must be a full MIME-Type or a format-specific short form.
这个选项定义了服务器应该使用的数据表示格式,它优先于任何HTTP内容协商标头,如Accept。它的值必须是一个完整的MIME-Type或一个特定格式的缩写形式。
For instance, we can use json as an abbreviation for application/json:
例如,我们可以用json作为application/json的缩写:。
.../CarModels?$format=json
This URL instructs our service to return data using JSON format, instead of XML, as we’ve seen before. When this option is not present, the server will use the value of the Accept header, if present. When neither is available, the server is free to choose any representation – usually XML or JSON.
这个URL指示我们的服务使用JSON格式返回数据,而不是像我们之前看到的XML。当这个选项不存在时,服务器将使用Accept 头的值,如果存在的话。当两者都不存在时,服务器可以自由选择任何表示方法–通常是XML或JSON。
Regarding JSON specifically, it’s fundamentally schemaless. However, OData 4.01 defines a JSON schema for metadata endpoints as well. This means that we can now write clients that can get totally rid of XML processing if they choose to do so.
具体到JSON,它从根本上说是没有模式的。然而,OData 4.01也为元数据端点定义了一种JSON模式。这意味着我们现在可以编写可以完全摆脱XML处理的客户端,如果他们选择这样做的话。
6. Conclusion
6.结语
In this brief introduction to OData, we’ve covered its basic semantics and how to perform simple data set navigation. Our follow-up article will continue where we left and go straight into the Olingo library. We’ll then see how to implement sample services using this library.
在这篇关于OData的简要介绍中,我们已经介绍了它的基本语义以及如何进行简单的数据集导航。我们的后续文章将继续我们离开的地方,直接进入Olingo库。然后我们将看到如何使用这个库来实现样本服务。
Code examples, as always, are available over on GitHub.
代码实例,一如既往,可在GitHub上获得。。