高级 HTTP 请求

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

Form 表单提交

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

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

以 Multipart 方式上传文件

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

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

  1. // app/controller/http.js
  2. class HttpController extends Controller {
  3. async upload() {
  4. const { ctx } = this;
  5. const result = await ctx.curl('https://httpbin.org/post', {
  6. method: 'POST',
  7. dataType: 'json',
  8. data: {
  9. foo: 'bar',
  10. },
  11. // 单文件上传
  12. files: __filename,
  13. // 多文件上传
  14. // files: {
  15. // file1: __filename,
  16. // file2: fs.createReadStream(__filename),
  17. // file3: Buffer.from('mock file content'),
  18. // },
  19. });
  20. ctx.body = result.data.files;
  21. // 响应最终会是类似以下的结果:
  22. // {
  23. // "file": "'use strict';\n\nconst For...."
  24. // }
  25. }
  26. }

以 Stream 方式上传文件

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

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