Rest API 最佳设计实践( 三 )


const express = require('express');const bodyParser = require('body-parser');const app = express();// existing usersconst users = [  { email: 'abc@foo.com' }]app.use(bodyParser.json());app.post('/users', (req, res) => {  const { email } = req.body;  const userExists = users.find(u => u.email === email);  if (userExists) {    return res.status(400).json({ error: 'User already exists' })  }  res.json(req.body);});app.listen(3000, () => console.log('server started'));在上面的代码中,我们users在给定电子邮件的数组中有一个现有用户的列表 。
然后,如果我们尝试使用email中已存在的值提交有效负载users,我们将获得一个 400 响应状态码和一条'User already exists'消息,让用户知道该用户已经存在 。使用该信息,用户可以通过将电子邮件更改为不存在的内容来更正操作 。
错误代码需要带有消息,以便维护者有足够的信息来解决问题,但攻击者不能使用错误内容来进行我们的攻击,如窃取信息或关闭系统 。
每当我们的 API 没有成功完成时,我们应该通过发送错误信息来帮助用户做出纠正措施,从而优雅地失败 。
允许过滤、排序和分页REST API 背后的数据库可能会变得非常大 。有时,有太多的数据不应该一次全部返回,因为它太慢或者会导致我们的系统崩溃 。因此,我们需要过滤项目的方法 。
我们还需要对数据进行分页的方法,以便一次只返回几个结果 。我们不想通过尝试一次获取所有请求的数据来占用资源太久 。
过滤和分页都通过减少服务器资源的使用来提高性能 。随着数据库中积累的数据越多,这些特征就越重要 。
这是一个小示例,其中 API 可以接受具有各种查询参数的查询字符串,以便我们按字段过滤项目:
const express = require('express');const bodyParser = require('body-parser');const app = express();// employees data in a databaseconst employees = [  { firstName: 'Jane', lastName: 'Smith', age: 20 },  //...  { firstName: 'John', lastName: 'Smith', age: 30 },  { firstName: 'Mary', lastName: 'Green', age: 50 },]app.use(bodyParser.json());app.get('/employees', (req, res) => {  const { firstName, lastName, age } = req.query;  let results = [...employees];  if (firstName) {    results = results.filter(r => r.firstName === firstName);  }  if (lastName) {    results = results.filter(r => r.lastName === lastName);  }  if (age) {    results = results.filter(r => +r.age === +age);  }  res.json(results);});app.listen(3000, () => console.log('server started'));在上面的代码中,我们有req.query获取查询参数的变量 。然后,我们通过使用 JavaScript 解构语法将各个查询参数解构为变量来提取属性值 。最后,我们filter继续使用每个查询参数值来定位我们想要返回的项目 。
完成此操作后,我们将返回results作为响应 。因此,当我们使用查询字符串向以下路径发出 GET 请求时:
/employees?lastName=Smith&age=30
我们得到:
[    {        "firstName": "John",        "lastName": "Smith",        "age": 30    }]作为我们过滤后的返回响应lastName和age 。
同样,我们可以接受page查询参数并返回位置 from (page - 1) * 20to中的一组条目page * 20 。
我们还可以在查询字符串中指定要排序的字段 。例如,我们可以从带有我们想要对其数据进行排序的字段的查询字符串中获取参数 。然后我们可以按这些单独的字段对它们进行排序 。
例如,我们可能想从如下 URL 中提取查询字符串:
http://example.com/articles?sort=+author,-datepublished
where+表示上升,-表示下降 。所以我们按照作者姓名的字母顺序,datepublished从最近到最近的排序 。
保持良好的安全实践客户端和服务器之间的大多数通信应该是私密的,因为我们经常发送和接收私人信息 。因此,必须使用 SSL/TLS 来确保安全 。
将 SSL 证书加载到服务器上并不难,而且成本免费或非常低 。没有理由不让我们的 REST API 通过安全通道而不是公开方式进行通信 。
人们不应该能够访问他们请求的更多信息 。例如,普通用户不应该能够访问其他用户的信息 。他们也不应该能够访问管理员的数据 。


推荐阅读