Http请求常用Content-Type使用指南

作者:JAY 2018-12-22

基础知识

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,媒体类型),由于接收方终端的差异(浏览器差异),场景比请求的复杂一点点,后面有机会再另外写一篇博客吧。


参考文献

  1. https://tools.ietf.org/html/rfc2616

  2. https://tools.ietf.org/html/rfc6838

  3. https://tools.ietf.org/html/rfc1867



本文未经许可禁止转载,如需转载请联系 JAY@oonne.com


TOP