Node中POST请求的正确处理方式

Node的 http 模块只对HTTP报文的头部进行了解析,然后触发 request 事件。如果请求中还带有内容部分(如 POST 请求,它具有报头和内容),内容部分需要用户自行接收和解析。

通过报头的 Transfer-EncodingContent-Length 即可判断请求中是否带有内容

字段名称

含义

Transfer-Encoding

指定报文主体的传输编码方式

Content-Length

报文主体的大小

写个方法判断是否有报文主体

const hasBody = function(req) {
  return  'transfer-encoding'  in  req.headers  ||  'content-length' in req.headers;
};

接收数据

报文内容部分会通过 data 事件触发,我们只需以流的方式处理即可,不要在订阅 data 事件的时候使用 += 的形式拼装数据,这样会乱码的。

function handle(req, res) {
  if (hasBody(req)) {
    var buffers = [];
    req.on('data', function (chunk) {
      buffers.push(chunk);
    });
    req.on('end', function () {
      const POST = Buffer.concat(buffers).toString();
    });
  }
}

1. POST发送的是表单的数据

如果在页面中使用表单提交一个post请求,我们的代码大概是这样的。

  <form action="/upload" method="post">
    <label for="username">用户名:</label>
    <input type="text" name="username" id="username" />
    <label for="password">密码:</label>
    <input type="password" name="password" id="password" />
    <input type="submit" />
  </form>

默认的表单提交,请求头中的 Content-Type 字段值为 application/x-www-form-urlencoded

Content-Type: application/x-www-form-urlencoded

写一个判断内容类型的方法

const mime = function (req) {
  const str = req.headers['content-type'] || '';
  return str.split(';')[0];
};

它的报文体内容跟查询字符串相同

username=Tom&password=123456

解析表单数据使用querystring模块中的parse方法

const querystring = require('querystring')
function handleForm (req, res) {
  const isFrom = mime(req) === 'application/x-www-form-urlencoded'
  if (hasBody(req) && isFrom) {
    var buffers = [];
    req.on('data', function (chunk) {
      buffers.push(chunk);
    });
    req.on('end', function () {
      let requestBody = Buffer.concat(buffers).toString();
      requestBody = querystring.parse(requestBody)
    });
  }
}

2. POST发送的是JSON的数据

如果在页面中使用axios发送post请求,我们的代码大概是这样的。

axios.post('/user', {
  username: 'Tom',
  password: '123456'
})

默认的JSON提交,请求头中的 Content-Type 字段值为 application/json,在 Content-Type 中可能还附带编码信息 charset=utf-8

Content-Type: application/json; charset=utf-8

它的报文体内容跟JSON格式的字符串相同

{
  "name": "Tom",
  "password": "123456"
}

解析JSON数据使用 JSON.parse 方法。

function handleJson (req, res) {
  const isJson = mime(req) === 'application/json'
  if (hasBody(req) && isJson) {
    var buffers = [];
    req.on('data', function (chunk) {
      buffers.push(chunk);
    });
    req.on('end', function () {
      let requestBody = Buffer.concat(buffers).toString();
      try {
        requestBody = JSON.parse(requestBody)
      } catch (error) {
        console.log(error)
      }
    });
  }
}

3. POST发送的是文件数据

如果在页面中使用表单提交文件请求,我们的代码大概是这样的。

  <form action="/upload" method="post" enctype="multipart/form-data">
    <input type="file" name="avatar" id="avatar">
    <input type="submit" />
  </form>

默认的上传文件提交,请求头中的 Content-Type 字段值为multipart/form-data,在 Content-Type 中可能还附带内容分隔符 boundary=----WebKitFormBoundary4Hsing01Izo2AHqv

Content-Type: multipart/form-data; boundary=----WebKitFormBoundary4Hsing01Izo2AHqv

先上传一个JS文件,看看报文主体里面的格式大概是这样的,包含文件信息和文件内容,有指定的分隔符包裹。

在这里插入图片描述

上传文件的时候是要区分文本文件和二进制文件,文本文件是要使用 utf8 编码(HTML,CSS,JavaScript),二进制文件是要使用 binary 编码(图片,视频,音频)

根据内容分隔符解析上传的图片,并且写入到文件中,下面代码暂时只处理图片格式的文件。

function handleFile(req, res) {
  const isFile = mime(req) === 'multipart/form-data'
  if (hasBody(req) && isFile) {
    var buffers = [];
    req.on('data', function (chunk) {
      buffers.push(chunk);
    });
    req.on('end', function () {
      // 处理文件名
      let requestBody = Buffer.concat(buffers).toString('binary');
      let file = querystring.parse(requestBody, '\r\n', ': ')
      let fileInfo = file['Content-Disposition']
      fileInfo = Buffer.from(fileInfo, 'binary').toString()
      let { filename } = querystring.parse(fileInfo, '; ', '=')
      filename = filename.slice(1, -1)
      filename = `./static/${filename}`
      // 处理内容
      let boundary = req.headers['content-type'].split('; ')[1].replace('boundary=', '');
      let contentType = file['Content-Type']
      if (!contentType.includes('image')) return
      let upperBoundary = requestBody.indexOf(contentType) + contentType.length;
      let shorterData = requestBody.substring(upperBoundary)
      let binaryDataAlmost = shorterData.trim()
      let binaryData = binaryDataAlmost.substring(0, binaryDataAlmost.indexOf(`--${boundary}--`))
      // 写入文件
      fs.writeFile(filename, binaryData, 'binary', (err) => {
        if (err) {
          console.log('上传失败')
        } else {
          console.log('上传成功', filename)
        }
      })
    });
  }
}

这就是所有处理POST请求的方式,你都学会了吗?

原创声明,本文系作者授权云+社区发表,未经许可,不得转载。

如有侵权,请联系 yunjia_community@tencent.com 删除。

编辑于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏Java程序猿部落

Java并发编程,深度探索J.U.C - AQS

java.util.concurrent(J.U.C)大大提高了并发性能,AQS 被认为是 J.U.C 的核心。

7740
来自专栏Jerry的SAP技术分享

CRM Web Client UI异步搜索介绍

版权声明:本文为博主汪子熙原创文章,未经博主允许不得转载。 https://jerry.bl...

5520
来自专栏Jerry的SAP技术分享

使用await和async关键字开发nodejs应用批量取出简书网站的文章标题和超链接

async用来表示函数是异步的,定义的函数会返回一个promise对象,可以使用then方法添加回调函数。

10120
来自专栏Jerry的SAP技术分享

HTTP状态码429的含义

今天我写了一个很简单的nodejs应用,使用module request同时向jianshu网站发起数个异步请求,通过分页的方式向简书请求我所有的文章列表。

79530
来自专栏Jerry的SAP技术分享

node-inspect命令行工具的调试使用方法

版权声明:本文为博主汪子熙原创文章,未经博主允许不得转载。 https://jerry.bl...

10520
来自专栏Node开发

Node.js实现桌面应用

从最开始我开始写文章就讲过Node.js与Java的优缺点,我当时说过,JAVA能做的如果非要使用Node.js最后肯定是能实现的,但是我们会考虑用什么更加适...

99840
来自专栏实战docker

CentOS7环境部署kubenetes1.12版本五部曲之四:安装dashboard

版权声明:欢迎转载,请注明出处,谢谢。 https://blog.csdn.net/boli...

16110
来自专栏黑泽君的专栏

大数据技术之_27_电商平台数据分析项目_01_大数据的框架回顾 + 大数据的企业应用

Hadoop job 提交简图 或 YARN 架构 或 YARN 工作机制 或 job 提交流程 0、job 提交简图

16820
来自专栏Jerry的SAP技术分享

在nodejs环境里使用浏览器环境下的document对象

我用nodejs写了一个简单的简书文章导出工具,将我本人的简书问题连同标题和超链接导出到本地。

20820
来自专栏Golang开发

Linkled List链表

链表通过指针将一组零散的内存块串联在一起。其中,我们把内存块称为链表的“结点”。为了将所有的结点串起来,每个链表的结点除了存储数据之外,还需要记录链上的下一个结...

12520

扫码关注云+社区

领取腾讯云代金券

年度创作总结 领取年终奖励