• 高级 HTTP 请求
    • Form 表单提交
    • 以 Multipart 方式上传文件
    • 以 Stream 方式上传文件

    高级 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. }