尝试了一下Express中的post请求,原以为和get请求是一样的。
axios的post请求的官方示例是这样的:
axios.post('/user', {
firstName: 'Fred',
lastName: 'Flintstone'
})
.then(function (response) {
console.log(response);
})
.catch(function (error) {
console.log(error);
});
但是结果发现,在后端无论如何都没有返回我需要的数据。
原来,POST方法提交的数据有四种编码方式。
1.application/x-www-form-urlencoded
这种方式是最常见的POST编码方式。浏览器的原生<form>
表单,如果不设置enctype
属性,那么最终就会以 application/x-www-form-urlencoded
方式提交数据。
很多时候,我们用 Ajax 提交数据时,也是使用这种方式。例如 JQuery 和 QWrap 的 Ajax,Content-Type 默认值都是「application/x-www-form-urlencoded;charset=utf-8」。
- multipart/form-data
我们使用表单上传文件时,必须让 <form> 表单的 enctype 等于 multipart/form-data。
这种方式一般用来上传文件,各大服务端语言对它也有着良好的支持。
上面提到的这两种 POST 数据的方式,都是浏览器原生支持的,而且现阶段标准中原生 <form> 表单也只支持这两种方式(通过 <form> 元素的 enctype
属性指定,默认为 application/x-www-form-urlencoded
。其实 enctype
还支持 text/plain
,不过用得非常少)。
随着越来越多的 Web 站点,尤其是 WebApp,全部使用 Ajax 进行数据交互之后,我们完全可以定义新的数据提交方式,给开发带来更多便利。
3 .application/json
现在越来越多的人把它作为请求头,用来告诉服务端消息主体是序列化后的 JSON 字符串。由于 JSON 规范的流行,除了低版本 IE 之外的各大浏览器都原生支持 JSON.stringify,服务端语言也都有处理 JSON 的函数,使用 JSON 不会遇上什么麻烦。
4.text/xml
它是一种使用 HTTP 作为传输协议,XML 作为编码方式的远程调用规范。XML-RPC 协议简单、功能够用,各种语言的实现都有。它的使用也很广泛,如 WordPress 的 XML-RPC Api,搜索引擎的 ping 服务等等。JavaScript 中,也有现成的库支持以这种方式进行数据交互,能很好的支持已有的 XML-RPC 服务。
相比之下,get方式的数据提交方式(编码方式)只有一种,就是application/x-www-form-urlencoding
我们的axios用的就是第三种编码方式,也就是application/json
,但是Express的post默认是第一种方式,所以我们就找不到我们要的数据了。
问题找到了,就要想办法解决问题。既然是前后端的数据编码方式不一样,那么只要想办法让他们一样就行了。
首先,把后端的接收格式改成application/json
。在Express中,我们需要用到一个叫做bodyParser的中间件。
引入body-parser:
$ npm install body-parser --save
基本使用:
var express = require('express')
//获取模块
var bodyParser = require('body-parser')
var app = express()
// 创建 application/json 解析
var jsonParser = bodyParser.json()
// 创建 application/x-www-form-urlencoded 解析
var urlencodedParser = bodyParser.urlencoded({ extended: false })
// POST /login 获取 URL编码的请求体
app.post('/login', urlencodedParser, function (req, res) {
if (!req.body) return res.sendStatus(400)
res.send('welcome, ' + req.body.username)
})
// POST /api/users 获取 JSON 编码的请求体
app.post('/api/users', jsonParser, function (req, res) {
if (!req.body) return res.sendStatus(400)
// create user in req.body
});
app.listen(3000);
对请求体的四种解析方式:
1. bodyParser.json(options): 解析json数据
2. bodyParser.raw(options): 解析二进制格式(Buffer流数据)
3. bodyParser.text(options): 解析文本数据
4. bodyParser.urlencoded(options): 解析UTF-8的编码的数据。
最后,bodyParser变量是对中间件的引用。请求体解析后,解析值都会被放到req.body
属性,所以直接拿出来就行了。
有的时候后端已经固定了,没有办法只能改前端。
前端:
this.$axios({
method:"post",
url:"/api/haveUser",
headers:{
'Content-type': 'application/x-www-form-urlencoded'
},
data:{
name:this.name,
password:this.password
},
transformRequest: [function (data) {
let ret = ''
for (let it in data) {
ret += encodeURIComponent(it) + '=' + encodeURIComponent(data[it]) + '&'
}
return ret
}],
}).then((res)=>{
console.log(res.data);
})
其中发挥关键作用的是headers与transformRequest。其中 headers 是设置即将被发送的自定义请求头。 transformRequest 允许在向服务器发送前,修改请求数据。这样操作之后,后台querystring.parse(decodeURIComponent(data))获取到的就是类似于{ name: 'w', password: 'w' }的对象。后台代码如下
后端:
app.post("/api/haveUser",function(req,res){
let haveUser=require("../api/server/user.js");
req.on("data",function(data){
let name=querystring.parse(decodeURIComponent(data)).name;
let password=querystring.parse(decodeURIComponent(data)).password;
console.log(name,password)
haveUser(name,password,res);
});
});
参考文献:
浏览器行为:Form表单提交
post提交数据的四种编码方式
Express 中间件----body-parser(五)