前言:
在做项目时,你们是否会遇到这样一个问题:
使用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时,边查阅资料,边思考推理过程的记录,若有错误之处,恳请大家在评论区斧正!