REST API的五种规则

本文提供了五个真正符合RESTful API含义的标准约束。

1.使用应用程序/ JSON媒体类型
API设计其中一个最常见的属性是使用的媒体类型应用程序/ JSON,或有时使用应用程序/ XML。 通过使用Jersey(JAX-RS),看起来经常是这样的:


@GET
@Produces(MediaType.APPLICATION_JSON)
public List<Product> getProducts() {
...
}

使用通用媒体类型(如JSON)基本上不是RESTful,因为REST要求消息是自描述的。 自我描述只是意味着数据的语义应该与数据本身一起传播。

以下两个选项的不同, 看看你喜欢哪一个:

Object getProducts();

或:

List<Product> getProducts();

第一个只是说它返回一个对象,在这种情况下,你必须知道该对象的实际类型是能够cast投射和使用它。 第二个明确说明它实际上是一个产品列表。

使用通用的媒体类型,需要让客户端掌握了JSON消息内部是什么才能知道如何使用它。这被称为隐性知识,主要问题是脆弱性,因为在别处做出的变化,会影响这里。

为了抵消脆弱性,最好是所有消息定义自己的媒体类型,就像代码中的所有类型定义自己的类一样。 这个定义不应该只包括语法,还应该包括如何解释消息,如何跟踪它的链接,如何处理它的形式,如何在屏幕上显示等等。

2.表示中的ID
通常,从服务器返回(或发送到服务器)的表示中包含数字形式的ID(标识符),类似于数据库表中的主键。 它看起来像这样:


{ "products": [
{
"id": 12,
"type": 103,
"name": "ACME Router D12"
},
{
"id": 13,
"type": 145,
"name": "5m UTP Cable"
},
...
]}

使用Java的一个例子,你喜欢哪个API?

List<Long> getProducts();
或:

List<Product> getProducts();
第一个给你一个标识符ID列表,你可以使用ID从某处获得一个真正的产品。 第二个返回显式对象引用,您可以使用直接它们,并直接对这些产品操作。

使用数字ID不是RESTful,这有两个不同的原因: 首先,它需要来自客户端的隐含知识,以便知道在哪里以及如何使用这些ID来获取产品。 这又增加了脆性和耦合。 其次,它没有使用HTTP的本地标识符(URI)来标识产品资源。

上面的例子应该这样更加RESTful:


{ "products": [
{
"id": "/product/12",
"type": "/type/103",
"name": "ACME Router D12"
},
{
"id": "/product/13",
"type": "/product/145",
"name": "5m UTP Cable"
},
...
]}

这虽然不是语法上的大变化,但它消除了对于客户端隐含地东西,非常明确。

3.文档涉及的路径和参数
很多时候,如果发布了有关API的任何文档,它包含了请求或响应中存在的路径,参数,字段的列表。 像一些Swagger 或 WADL围绕这些概念被明确为中心。 从这些工具生成的规范和文档都如下所示:


Path: "/products"
- GET: Get all products
Path:
"/product/{id}"
- GET: Get the product with given id
- DELETE: Delete the product
...


上述文档说明是参考Swagger的PetStore例子。

这不是原始意义上的RESTful,因为它假定客户端必须知道所有这些路径和参数,以及它们的含义。 但是,如果表示使用各种说明链接和详细列表说明,就像上面案例一样,这些说明信息都不与客户端相关,因为客户端只是跟随引导到任何地方。

那么,规范和文档应该做什么,描述所有定义的媒体类型,很像Java代码的API文档。 一个非常小的示例将如下所示:


Type: "application/vnd.acme.productlist+json"
- Represents a paged product listing in the following JSON format:
{
"products": [
{
"self": "/product/123",
"name": "ACME Router"
},
...
],
"nextPage": "/products?page=3",
"previousPage": "/products?page=1"
}
- The
"self" link in Products leads to the product listed
- The global
"nextPage" link, if present, leads to the next page
of the product listing
- Similarly, the
"previousPage" to the previous (if not on first page)

它没有具体提及这种表示可能在何处; 它只是描述了一旦收到它如何处理它。 它也没有提到如何生成到下一页或上一页的路径或如何附加参数或路径片段,它只是提供供客户端使用组合的链接。 这些链接是如何创建的并不重要,事实上,服务器可以决定更改这些实现细节,恕不另行通知。

4. URI模板
URI模板是带有占位符的URI。 在命名替换变量的帮助下,它们可以用于生成特定的URI。 他们看起来像这样:


/product/{id}
/products?page={pageNumber}
/products?startIndex={startIndex}&endIndex={endIndex}


这些通常用于规范或文档中以描述资源位于何处,并且期望客户端知道这些模板并且可以生成特定资源的完整URI。

这是因为这些资源没有被引用(链接到)其他资源,所以客户端需要以某种方式手动猜测资源的URI。 这种方法的问题与以前相同;这使得客户端容易崩溃,如果一些规则改变或者如果服务器决定引入不同的资源结构。

解决办法就是让这些资源能在某个地方被链接引用。

5. URI中的版本号
很多厂商把自己的API的版本放入URI中:


/api/v1/products
/api/products_v1
/api/products?version=v1

从原始的REST风格的角度来看,这些看起来非常可疑,因为URI标识了应该代表一些业务相关概念的资源。这些业务概念可能包括人员,客户,产品,产品列表,订单等。如果您问业务人员,“什么是客户版本2? 他或她可能会非常奇怪地看着你。

URI的实际优点是不会随着版本升级而改变,在升级单个服务时具有更多的灵活性,能够将链接设为书签而不改变它们等。

可以在表示形式或消息的格式中指定版本号:
application/vnd.acme.product-v1+json

或者使用媒体类型定义也可以定义额外参数:

application/vnd.acme.product+json; version=1


总结
REST中至少应该有以下两个含义:

1.复杂集成问题的原始含义,这是通过对架构的严格约束解决的。

2.服务中通俗意义,这是通过HTTP发布某种基于JSON接口的某种形式实现的。

5 Signs That Your REST API Isn't RESTful