高级 HTTP 请求

高级 HTTP 请求

在真实的应用场景下,还是会包含一些较为复杂的 HTTP 请求。

Form 表单提交

面向浏览器设计的 Form 表单(不包含文件)提交接口,通常都要求以 content-type: application/x-www-form-urlencoded的格式提交请求数据。

// app/controller/npm.js
class NpmController extends Controller {
  async submit() {
    const ctx = this.ctx;
    const result = await ctx.curl('https://httpbin.org/post', {
      // 必须指定 method,支持 POST,PUT 和 DELETE
      method: 'POST',
      // 不需要设置 contentType,HttpClient 会默认以 application/x-www-form-urlencoded 格式发送请求
      data: {
        now: Date.now(),
        foo: 'bar',
      },
      // 明确告诉 HttpClient 以 JSON 格式处理响应 body
      dataType: 'json',
    });
    ctx.body = result.data.form;
    // 响应最终会是类似以下的结果:
    // {
    //   "foo": "bar",
    //   "now": "1483864184348"
    // }
  }
}

以 Multipart 方式上传文件

当一个 Form 表单提交包含文件的时候,请求数据格式就必须以 multipart/form-data进行提交了。

[urllib] 内置了 [formstream] 模块来帮助我们生成可以被消费的 form 对象。

// app/controller/http.js
class HttpController extends Controller {
  async upload() {
    const { ctx } = this;

    const result = await ctx.curl('https://httpbin.org/post', {
      method: 'POST',
      dataType: 'json',
      data: {
        foo: 'bar',
      },

      // 单文件上传
      files: __filename,

      // 多文件上传
      // files: {
      //   file1: __filename,
      //   file2: fs.createReadStream(__filename),
      //   file3: Buffer.from('mock file content'),
      // },
    });

    ctx.body = result.data.files;
    // 响应最终会是类似以下的结果:
    // {
    //   "file": "'use strict';\n\nconst For...."
    // }
  }
}

以 Stream 方式上传文件

其实,在 Node.js 的世界里面,Stream 才是主流。如果服务端支持流式上传,最友好的方式还是直接发送 Stream。Stream 实际会以 Transfer-Encoding: chunked 传输编码格式发送,这个转换是 [HTTP] 模块自动实现的。

// app/controller/npm.js
const fs = require('fs');
const FormStream = require('formstream');
class NpmController extends Controller {
  async uploadByStream() {
    const ctx = this.ctx;
    // 上传当前文件本身用于测试
    const fileStream = fs.createReadStream(__filename);
    // httpbin.org 不支持 stream 模式,使用本地 stream 接口代替
    const url = `${ctx.protocol}://${ctx.host}/stream`;
    const result = await ctx.curl(url, {
      // 必须指定 method,支持 POST,PUT
      method: 'POST',
      // 以 stream 模式提交
      stream: fileStream,
    });
    ctx.status = result.status;
    ctx.set(result.headers);
    ctx.body = result.data;
    // 响应最终会是类似以下的结果:
    // {"streamSize":574}
  }
}