美文网首页
KOA 与 REST API

KOA 与 REST API

作者: liwuwuzhi | 来源:发表于2021-01-07 14:49 被阅读0次

REST API 规范

REST请求只是一种请求类型和响应类型均为JSON的HTTP请求。

编写REST API,实际上就是编写处理HTTP请求的async函数,不过,REST请求和普通的HTTP请求有几个特殊的地方:

REST请求仍然是标准的HTTP请求,但是,除了GET请求外,POST、PUT等请求的body是JSON数据格式,请求的Content-Type为application/json;
REST响应返回的结果是JSON数据格式,因此,响应的Content-Type也是application/json。
REST规范定义了资源的通用访问格式,虽然它不是一个强制要求,但遵守该规范可以让人易于理解。

例如,商品Product就是一种资源:

  • GET /api/products 获取所有Product;

  • GET /api/products/123 而获取某个指定的Product ,指定id为123;

  • POST /api/products 新建一个Product,JSON数据包含在body中;

  • PUT /api/products/123 更新一个Product,更新id为123;

  • DELETE /api/products/123 删除一个Product使用DELETE请求,删除id为123;

  • GET /api/products/123/reviews 资源还可以按层次组织。如,获取某个Product的所有评论;

  • GET /api/products/123/reviews?page=2&size=10&sort=time 也可通过参数限制返回的结果集。如,返回第2页评论,每页10项,按时间排序。

REST API 编写

使用REST虽然非常简单,但是,设计一套合理的REST框架却需要仔细考虑很多问题。

问题一:如何组织URL

在实际工程中,一个Web应用既有REST,还有MVC,可能还需要集成其他第三方系统。如何组织URL?
一个简单的方法是通过固定的前缀区分。例如,/static/ 开头的URL是静态资源文件,类似的,/api/ 开头的URL就是REST API,其他URL是普通的MVC请求。

问题二:如何统一输出REST
  • REST API的返回值全部是object对象
    为方便客户端处理结果,只要是请求成功,不管是查询错误还是查询异常,都返回JSON数据,而不是简单的 number、boolean、null或者数组;
  • REST API必须使用前缀/api/
    如果需要对请求做某些统一处理,就可以通过 /api/ 来判断当前请求是否是一个 REST 请求。
问题三:如何处理错误

这个问题实际上有两部分。

HTTP请求可能发生的错误
当REST API请求出错时,像403,404,500等错误,我们如何返回错误信息?
针对这种类型的错误,第一类的错误实际上客户端可以识别,并且我们也无法操控HTTP服务器的错误码。

业务逻辑的错误
当客户端收到 REST 响应后,如何判断是成功还是错误?
例如,输入了不合法的Email地址,试图删除一个不存在的Product,等等。这种类型的错误完全可以通过JSON返回给客户端,这样,客户端可以根据错误信息提示用户“Email不合法”等,以便用户修复后重新请求API。例如:

{
    "code": "0",
    "message": "Bad email address"
}

REST架构本身同样没有标准的错误码定义一说,因此,有的Web应用使用数字1000、1001……作为错误码。

不推荐混合其他HTTP错误码。例如,使用401响应“登录失败”,使用403响应“权限不够”。这会使客户端无法有效识别HTTP错误码和业务错误,其原因在于HTTP协议定义的错误码十分偏向底层,而REST API属于“高层”协议,不应该复用底层的错误码。

koa 处理 REST

使用koa作为Web框架处理HTTP请求,因此,我们可以在koa中响应并处理REST请求。

我来看下koa一个简单的路由请求案例:

const Koa = require('koa')
const router = require('koa-router')() // 处理路由映射
const bodyParser = require('koa-bodyparser') // 处理提交请求
const app = new Koa()

// add bodyparser
app.use(bodyParser())

// add url-route:
router.get('/', async (ctx, next) => {
  ctx.response.body = '<h1>Hello, koa2!!!</h1>'
});

router.get('/hello/:name', async (ctx, next) => {
  var name = ctx.params.name;
  ctx.response.body = `<h1>Hello, ${name}!</h1>`
});

router.get('/login', async (ctx, next) => {
  ctx.response.body = `
      <h1>Index</h1>
      <form action="/signin" method="post">
          <p>Name: <input name="name" value="koa"></p>
          <p>Password: <input name="password" type="password"></p>
          <p><input type="submit" value="Submit"></p>
      </form>`
});

router.post('/signin', async (ctx, next) => {
  const { name, password } = ctx.request.body
  if (name === 'koa' && password === '123456') {
    const result = {
      message: 'login success',
      username: name
    }
    ctx.response.body = result
  } else {
    ctx.response.body = `<h1>Login failed!</h1>`
  }
})

// add router middleware:
app.use(router.routes());

app.listen(3000);
console.log('app started at port 3000...');

上面代码中有两个关键的 middleware:koa-routerkoa-bodyparser

koa-router

为处理URL,我们需要引入koa-router这个middleware,让它负责处理URL映射,根据不同的URL调用不同的处理函数。

安装、引入。

然后就使用 router.get('/path', async fn) 来注册一个GET请求。如果API路径带有参数,参数必须用 : 表示,参数通过 ctx.params.来访问。
例如上面例子中的 /hello/:name,参数通过 ctx.params.name 访问。
客户端传递的URL可能就是 /hello/007,那么参数 name 对应的值就是 007,用 ctx.params.name 来获取。

类似的,如果API路径有多个参数,例如,/api/products/:pid/reviews/:rid,则这两个参数分别用
ctx.params.pidctx.params.rid 获取。

这个功能由 koa-router 这个middleware提供。

注意:API路径的参数永远是字符串!

koa-bodyparser

如果要处理post请求,可以用 router.post('/path', async fn)
用post请求处理URL时,我们会遇到一个问题:post请求通常会发送一个表单,或者JSON,它作为request的body发送,但无论是Node.js提供的原始request对象,还是koa提供的request对象,都不提供解析request的body的功能!

所以,我们需要引入另一个middleware来解析原始request请求,然后,把解析后的参数,绑定到 ctx.request.body 中。
koa-bodyparser就是用来干这个活的。

注意,由于middleware的顺序很重要,这个 koa-bodyparser 必须在router之前被注册到app对象上。如果 ctx.request.body 为 undefined,说明缺少middleware,或者middleware没有正确配置。

koa-bodyparser 给 koa 安装了一个解析HTTP请求body的处理函数。
如果客户端传递了JSON格式的数据(例如,POST、PUT等请求),就可以通过 ctx.request.body 直接访问已经反序列化的 JavaScript 对象。
如果要返回JSON格式的数据到客户端,只需要给 ctx.response.body 赋值一个JavaScript对象,koa会自动把该对象序列化为JSON并输出到客户端。

响应 response.type

在 koa内部,koa 会根据 ctx.response.body 响应体容格式的不同设置默认的Content-Type。
将响应体设置为以下之一 并 赋 Content-Type 默认值:

  • string 写入
    Content-Type 默认为 text/html 或 text/plain, 同时默认字符集是 utf-8。Content-Length 字段也是如此。

  • Buffer 写入
    Content-Type 默认为 application/octet-stream, 并且 Content-Length 字段也是如此。

  • Stream 管道
    Content-Type 默认为 application/octet-stream

  • Object || Array JSON 字符串化
    Content-Type 默认为 application/json. 这包括普通的对象 { foo: 'bar' } 和数组 ['foo', 'bar']

  • null 无内容响应
    Content-Type ,无

我们也可以通过 response.type 自行设置 Content-Type:
【 获取】
获取响应 Content-Type, 获取到的值不含 "charset" 等参数:
const ct = ctx.type // => "image/png"
【 设置】
设置响应 Content-Type :
ctx.type = 'text/plain; charset=utf-8';
ctx.type = 'image/png';
ctx.type = '.png';
ctx.type = 'png';

如果不设置字符串charset,将默认是 "utf-8"。
如果你想覆盖 charset, 使用 ctx.set('Content-Type', 'text/html') 将响应头字段设置为直接值。

例如上面例中的get请求:

router.get('/', async (ctx, next) => {
  ctx.response.type = 'text/html'; // 'text/html; charset=utf-8'

  /*或*/
  ctx.set('Content-Type', 'text/html') //  'text/html'

  ctx.response.body = '<h1>Hello, koa2!!!</h1>'
});

相关文章

网友评论

      本文标题:KOA 与 REST API

      本文链接:https://www.haomeiwen.com/subject/rcjwoktx.html