解决:node后端接收到axios的post请求体竟为空?

2022-02-14 10:11:03 浏览数 (1)

前言:

在做项目时,你们是否会遇到这样一个问题:

使用axios发送post请求,传入了Object格式的参数,在node后端req.body接收到的参数为空,但是网页上抓包检查时,发现请求的body确实是携带了参数的?

今天,我在写vue node项目时,在提交登录信息(username,password)到后端时,就遇到了这个小bug,花了我一个半小时的时间,才搞出了个所以然来。

因此,决定写下这篇文章来给前后端技术小白们避雷


BUG情境还原:

先介绍一下我后端node使用到的包

代码语言:javascript复制
"@escook/express-joi": "^1.1.1", //进行表单验证相关包 
"cors": "^2.8.5", //解决请求跨域问题相关包 
"express": "^4.17.2",//node.js的web应用框架 
"joi": "^17.6.0", //定义表单验证规则的包 
"mysql": "^2.18.1" //数据库相关包

app.js中部分基本配置:

代码语言:javascript复制
// 配置解析 数据格式为表单数据的请求体 的中间件 
app.use(express.urlencoded({ extended: false })) 
// 导入 cors 中间件 
const cors = require('cors') 
// 将 cors 注册为全局中间件 
app.use(cors())

后端使用了express搭建服务器,并使用了cors解决前端请求跨域问题,并配置了joi的表单验证,每次向api提交的表单数据,都会先经过表单验证的中间件,其中验证规则设置了username和password都是required

前端vue组件中写的登录请求函数:

平平无奇的axios进行post提交表单的代码

怎么样,乍一看是不是万无一失?(不是)

于是我去页面进行了测试(Later....

我直接蒟蒻问号???

显然,是我的表单验证中间件没有拿到前端发送过去username信息,于是我开始了漫长的debug。

首先,我使用中间件,在数据提交到后台时,先在控制台打印一下req.body这个对象。

显而易见,服务器中req.body请求体中没有任何参数。但是页面确实是提交了数据呀?

于是我在页面F12进行网络抓包来查看发出去的request请求

抓到的包中请求体确实携带了页面发送的参数,然后我就开始意识到事情的不对劲了,开始在网上搜索答案。

后来,我把问题锁定到了axios请求机制服务器对请求体数据解析

之后尝试过在axios请求函数中,在header中配置内容数据格式为'Content-Type': 'application/x-www-form-urlencoded',依然没用

经过漫长的网上冲浪,并查了一下axios的源码,我发现

axios的文档上有这样一句话

这就能解释为什么我第一次发送的是obj对象数据,请求体携带的确是json格式的数据,说明axios会自动转换数据为json格式

后来我又在源码上看到了转换请求体参数格式相关代码

代码语言:javascript复制
if(utils.isURLSearchParams(data){ 
  setContentTypeIfUnset(headers, 'application/x-www-form-urlencoded'); 
  return data.tostring(); 
} 
if(utils.isObject(data){ 
  setContentTypeIfUnset(headers, 'application/json;charset=utf-8'); 
  return JSON.stringfy(data); 
}

显然,axios在发送请求时,如果参数对象data不是表单数据格式对象,就会默认把数据转为json字符串,放到请求体中的。

所以我之前发过去的obj对象,被axios自动转化为了json字符串

但是到此为止,感觉还是没有任何环节有致命问题呀?将JSON字符串格式的参数发给服务器,确实应该也没什么问题呀?

于是我就重新回到服务器的配置代码上来,显然,应该是服务器无法解析request请求的请求体body中JSON字符串的数据。

这让我想到了我最开始配置的这行代码

代码语言:javascript复制
// 配置解析 数据格式为表单数据的请求体 的中间件 
app.use(express.urlencoded({ extended: false }))

expres服务器默认无法解析数据格式为表单数据的请求体,因此express才提供了这个中间件,让我们配置,从而能够解析req.body 中表单格式数据。而这个中间件内部,其实是在配置body-parser属性,所以我的每个request请求都是要经过这个过滤器解析的,也就是说,这个中间件不能解析json格式字符串????

经过网上查阅,我找到了如下解释

body-parser的urlencoded方法顾名思义就是把传来的数据当做url来处理,也就是像querystring一样,所以对于传过来的json数据,没有识别到切割key和value的标志,就把所有都当做key来处理

真相大白。body-parser无法解析请求体中的JSON字符串,所以当收到JSON格式的参数时,因无法解析,所以req.body就为空了


解决:

既然body-parser只能解析序列化的表单数据格式,即“?username=username&password=passwrod”

我们只要将参数对象序列化成表单数据格式,再发送就好了。

这里要用到axios提供的 qs 库

qs库

介绍:

qs是axios自带的一个库

功能:

里面的stringify方法可以将一个json对象直接转为(以?和&符连接的形式)。 在开发中,发送请求的入参大多是一个对象。在发送时,如果该请求为get请求,就需要对参数进行转化。使用该库,就可以自动转化,而不需要手动去拼接

所以我只要将我的参数对象通过qs的stringfy方法转换为表单数据格式,再通过axios发送给服务器,body-parser就能解析成key,value的键值对形式,放入req.body中。

登录请求代码更改

服务器控制台打印的req.body对象


总结:

到这里,问题就解决啦!对于像我这种开发经验并不丰富的小白来说,这种bug确实非常致命,需要我用好几个小时去彻底搞懂,然后再花时间写一篇文章来记录。当然,项目开发遇到bug很正常,只要耐心地去查阅资料,一点点地把搞懂,直到透彻,那么就会有很大的收获!

如果看官们觉得这篇文章对你们有帮助的话,麻烦点个赞同哦~

之后我还会陆续更新算法前后端技术的文章,欢迎大家关注支持!

以上内容只是我在debug时,边查阅资料,边思考推理过程的记录,若有错误之处,恳请大家在评论区斧正!

0 人点赞