美文网首页
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