基础知识
Http请求消息体的格式为:request-line(请求行)、header(头部)、blank-line(空格行)和body(消息体)。Request-line声明了请求地址、方法和版本号,header部分声明请求的附加信息,Blank-line(空格行)用于分隔header和body,body部分是请求的消息体,可以为空。如下图所示:
Body不是必须的,也没有格式要求。通常Get类型的请求,body部分可以为空,参数在header中以key-value格式传输即可;Post类型的请求,通过body传输数据。由于body可以传输任意格式的数据格式,为了让接收方知道怎么解析数据,发送方需要在header中,使用Content-Type字段声明body部分的数据格式。
常见的Content-Type
以下介绍如无特殊说明,发送端默认是浏览器,接受端默认是使用Express搭建的Http服务。其他终端使用http时,推荐参照浏览器的标准。
application/x-www-form-urlencoded
HTML默认的表单提交方式,发送urlencoded格式的纯文本数据。发送如下:
<form name="form" action="test-form" method="post">
<input type="text" name="key1" value="value1">
<input type="text" name="key2" value="value2">
<input type="submit" value="Submit">
</form>
点击提交按钮,表单发起的http请求header里自动带着“Content-Type: application/x-www-form-urlencoded”,Body中的数据以“key1=value1&key2=value2”的文本格式传输。
如果使用Axios发送json数据,需要把json先格式化成“key1=value1&key2=value2”的形式。推荐使用一个叫QS的库,使用方式如下:
import axios from 'axios'
import qs from 'qs'
let options = {
headers: {
'Content-Type': 'application/x-www-form-urlencoded',
}
}
let Request = axios.post(reqUrl, qs.stringify(params), options).then((response)=>{
return response.data
})
服务端收到请求之后,通常需要把body的内容转换成json使用。有些框架自带了处理,但Express需要引入中间件来处理。推荐使用body-parser,使用方法如下:
let express = require('express');
let app = express();
let bodyParser = require('body-parser');
module.exports = app;
app.use(bodyParser.urlencoded());
app.post('/test-form', function (req, res) {
console.log(req.body);
res.send(req.body);
});
multipart/form-data
Http协议最开始是不支持文件上传的。直到1995年发布的规范《RFC 1867-Form-based File Upload in HTML》,新增了一个Content-Type类型用于发送文件,这就是我们今天熟知的multipart/form-data。multipart是“多部分”的意思,意味着body中的数据允许有多部分组成,可以同时传递文本数据和二进制数据。
使用HTML表单提交时,只需要在form标签内加上enctype="multipart/form-data",如下:
<form name="form" action="test-data" method="post" enctype="multipart/form-data">
<input type="file" id="file"/>
<input type="submit" value="Submit" />
</form>
点击提交按钮,表单发起的http请求header里带着“Content-Type: multipart/form-data; boundary=----WebKitFormBoundaryG4tAWTT3yikZt0WN”。前面的说明了使用multipart/form-data格式传递数据,而boundary后跟的一串字符表示的是分隔符。分隔符发起请求是会重新生成,所以每次请求都是不一样的。查看Body中的数据如下:
------WebKitFormBoundaryG4tAWTT3yikZt0WN
Content-Disposition: form-data; name="key1"
value1
------WebKitFormBoundaryG4tAWTT3yikZt0WN
Content-Disposition: form-data; name="key2"; filename="test.zip"
Content-Type: application/octet-stream
[文件二进制数据]
------WebKitFormBoundaryG4tAWTT3yikZt0WN--
可以看到请求的内容使用boundary分隔符进行区分,文本内容和文件可以同时发送。对于每个key,在分隔符后都会标明数据类型和格式等,然后使用空行隔开,再附带value值。
使用Axios发送文件时,只需要在headers里加上“'Content-Type': 'multipart/form-data'”即可,不需要对数据进行格式化。
Express接收文件依然需要引入中间件来处理。推荐使用multer,使用方法如下:
let express = require('express');
let app = express();
let multer = require('multer');
module.exports = app;
let upload = multer({ dest: 'upload/' });
app.post('/test-data', upload.single('key2'), function (req, res, next) {
let file = req.file;
console.log(file);
console.log(req.body);
res.json({
key1: req.body.key1,
key2: file.filename
});
});
上传的文件会储存在dest声明的路径里。req.file可以获取到上传的文件信息,req.body可以同时获取到其他文本格式传输的数据。
application/json
接口最常用的格式,body的内容就直接是json格式的文本。发送时仅需要在headers里加上“'Content-Type': 'application/json'”即可,如下:
import axios from 'axios'
let options = {
headers: {
'Content-Type': 'application/json',
}
}
let Request = axios.post(reqUrl, params, options).then((response)=>{
return response.data
})
接收时也可以用body-parser,使用方法如下:
let express = require('express');
let app = express();
let bodyParser = require('body-parser');
module.exports = app;
app.use(bodyParser.json());
app.post('/test-json', function (req, res) {
console.log(req.body);
res.send(req.body);
});
text/plain
纯文本格式,body的内容就只是文本。发送仅需在headers里加上“'Content-Type': 'text/plain'”即可。接收时也可以用body-parser,使用方法如下:
let express = require('express');
let app = express();
let bodyParser = require('body-parser');
module.exports = app;
app.use(bodyParser.text());
app.post('/test-text', function (req, res) {
console.log(req.body);
res.send(req.body);
});
类似的有text/html,内容是html格式,传输网页时会用到。还有text/xml,内容是XML,XML-RPC之类的规范会用到。
总结
本文介绍了Http请求时候的几种常用的Content-Type。
Http响应也会带Content-Type,原理和请求的一致,目的是让接收方知道怎么解析数据。响应的Content-Type又被称为Mime-type(Multipurpose Internet Mail Extensions,媒体类型),由于接收方终端的差异(浏览器差异),场景比请求的复杂一点点,后面有机会再另外写一篇博客吧。
参考文献
- https://tools.ietf.org/html/rfc2616
- https://tools.ietf.org/html/rfc6838
- https://tools.ietf.org/html/rfc1867
本文未经许可禁止转载,如需转载关注微信公众号【工程师加一】并留言。