什么是 RESTful API?
REST(Representational State Transfer)是一种架构风格,它基于 HTTP 协议,用于 Web 应用程序的设计和开发。RESTful API 是一种符合 REST 风格的 API,它的设计原则包括:
- 资源的标识:每个资源都有一个唯一的标识符,比如 URL。
- 资源的操作:使用 HTTP 方法来表示对资源的操作,比如 GET、POST、PUT、DELETE。
- 自描述消息:客户端和服务端之间的通信应该是无状态的,并且包含足够的信息,使得客户端能够理解服务端返回的响应。
HATEOAS 是什么?
HATEOAS(Hypermedia as the Engine of Application State)是 RESTful API 中的一种设计原则,它表示客户端可以通过服务端返回的响应中的链接来发现和操作资源。换句话说,HATEOAS 可以让客户端通过链接获取到下一步可以做什么,而不需要事先了解服务端的 API。
HATEOAS 的实现方式
HATEOAS 的实现方式有很多种,下面介绍一些常用的方式。
HAL(Hypertext Application Language)
HAL 是一种基于 JSON 的媒体类型,它使用 _links 和 _embedded 字段来表示链接和嵌入的资源。下面是一个例子:
// www.javascriptcn.com code example
{
"_links": {
"self": {
"href": "https://api.example.com/orders"
},
"next": {
"href": "https://api.example.com/orders?page=2"
}
},
"_embedded": {
"orders": [
{
"id": 1,
"status": "pending",
"_links": {
"self": {
"href": "https://api.example.com/orders/1"
},
"cancel": {
"href": "https://api.example.com/orders/1/cancel"
}
}
},
{
"id": 2,
"status": "shipped",
"_links": {
"self": {
"href": "https://api.example.com/orders/2"
},
"return": {
"href": "https://api.example.com/orders/2/return"
}
}
}
]
}
}在这个例子中,_links 表示链接,_embedded 表示嵌入的资源。客户端可以通过链接来发现和操作资源,比如获取下一页订单、取消订单、退货订单等。
JSON API
JSON API 是一种基于 JSON 的媒体类型,它使用 links 和 relationships 字段来表示链接和关系。下面是一个例子:
// www.javascriptcn.com code example
{
"data": [
{
"type": "orders",
"id": "1",
"attributes": {
"status": "pending"
},
"links": {
"self": "https://api.example.com/orders/1"
},
"relationships": {
"customer": {
"links": {
"self": "https://api.example.com/orders/1/relationships/customer",
"related": "https://api.example.com/orders/1/customer"
},
"data": {
"type": "customers",
"id": "1"
}
}
}
}
],
"links": {
"self": "https://api.example.com/orders"
}
}在这个例子中,links 表示链接,relationships 表示关系。客户端可以通过链接来发现和操作资源,比如获取订单的客户信息。
Siren
Siren 是一种基于 JSON 的媒体类型,它使用 links、actions 和 entities 字段来表示链接、操作和实体。下面是一个例子:
// www.javascriptcn.com code example
{
"class": [
"order"
],
"properties": {
"id": 1,
"status": "pending"
},
"links": [
{
"rel": [
"self"
],
"href": "https://api.example.com/orders/1"
},
{
"rel": [
"next"
],
"href": "https://api.example.com/orders?page=2"
}
],
"actions": [
{
"name": "cancel",
"method": "POST",
"href": "https://api.example.com/orders/1/cancel",
"fields": [
{
"name": "reason",
"type": "text"
}
]
}
],
"entities": [
{
"class": [
"customer"
],
"rel": [
"customer"
],
"href": "https://api.example.com/customers/1"
}
]
}在这个例子中,links 表示链接,actions 表示操作,entities 表示实体。客户端可以通过链接来发现和操作资源,比如获取下一页订单、取消订单、获取订单的客户信息等。
HATEOAS 的最佳实践
根据 RESTful API 的设计原则,HATEOAS 的最佳实践应该包括以下几个方面:
提供清晰的文档
服务端应该提供清晰的文档,包括资源的标识、操作和链接的格式等信息。客户端可以通过文档来了解服务端的 API,从而发现和操作资源。
使用标准的媒体类型
服务端应该使用标准的媒体类型,比如 HAL、JSON API、Siren 等。客户端可以通过媒体类型来了解服务端返回的响应的格式,从而发现和操作资源。
提供链接和嵌入的资源
服务端应该提供链接和嵌入的资源,让客户端能够发现和操作资源。链接应该包含足够的信息,比如资源的标识、操作和参数等。嵌入的资源应该包含完整的信息,比如资源的属性和关系等。
遵守 RESTful API 的设计原则
服务端和客户端都应该遵守 RESTful API 的设计原则,包括资源的标识、操作和自描述消息。服务端应该使用标准的 HTTP 方法来表示对资源的操作,客户端应该使用标准的 HTTP 状态码来表示对资源的操作结果。
示例代码
下面是一个使用 HAL 实现 HATEOAS 的示例代码:
// www.javascriptcn.com code example
const express = require('express');
const hal = require('hal');
const app = express();
const orders = [
{ id: 1, status: 'pending' },
{ id: 2, status: 'shipped' },
];
app.get('/orders', (req, res) => {
const resource = new hal.Resource({ orders }, '/orders');
resource.link('next', '/orders?page=2');
orders.forEach((order) => {
const orderResource = new hal.Resource(order, `/orders/${order.id}`);
orderResource.link('cancel', `/orders/${order.id}/cancel`);
resource.embed('orders', orderResource);
});
res.json(resource);
});
app.post('/orders/:id/cancel', (req, res) => {
const order = orders.find((order) => order.id === parseInt(req.params.id));
if (!order) {
res.status(404).send('Order not found');
} else if (order.status !== 'pending') {
res.status(400).send('Order cannot be cancelled');
} else {
order.status = 'cancelled';
res.json(order);
}
});
app.listen(3000, () => {
console.log('Server started on port 3000');
});在这个例子中,服务端使用了 hal 库来实现 HAL 格式的响应。客户端可以通过链接来发现和操作资源,比如获取下一页订单、取消订单等。服务端和客户端都遵守了 RESTful API 的设计原则,比如使用标准的 HTTP 方法和状态码来表示对资源的操作和操作结果。
来源:JavaScript中文网 ,转载请注明来源 https://www.javascriptcn.com/post/67d2501aa941bf7134460d2a