http的Content-Type字段笔记

HTTP/1.1协议规定的HTTP请求方法有OPTIONSGETHEADPOSTPUTDELETETRACECONNECT这几种, 用的最多的是GETPOST, 这里主要说一下提交请求时的请求头中Content-Type字段

# http请求结构

http请求分为三个部分: 状态行, 请求头和消息主体, 结构如下:

<method> <request url> <version>
<headers>

<entity-body>

# Content-Type类型

Content-Type有如下常见的类型:

由于GET方式的数据实际上是以QueryString的方式放在<request url>中的(非ASCII字符会被转码), 例如'https://www.example.com?key1=value1&key2=value2', 所以对GET讨论Content-Type没有意义

http协议规定POST提交的数据必须放在消息主体(entity-body)中,但协议并没有规定数据必须使用什么编码方式。实际上,开发者完全可以自己决定消息主体的格式,只要最后发送的HTTP请求满足上面的格式就可以。

服务端通常是根据请求头(headers)中的Content-Type字段来获知请求中的消息主体是用何种方式编码,再对主体进行解析。所以有必要了解Content-Type的内容. 目前在POST请求中所使用的Content-Type主要有如下四种类型: application/x-www-urlencoded, multipart/form-data, application/json, text/xml, 下面详细说一下这四种类型.

# application/x-www-urlencoded

这应该是最常见的POST提交数据的方式了。浏览器的原生<form>表单,如果不设置enctype属性,那么默认就会以这种方式提交数据。这种方式会将表单中的数据按照key1=value1&key2=value2的形式连接成字符串, 同时会将出现的非ASCII字符进行编码, 编码方式可以参考encodeURIComponent()函数, 例如下面这个表单提交时的数据结构:

POST http://www.example.com HTTP/1.1
Content-Type: application/x-www-form-urlencoded;charset=utf-8

title=test&sub%5B%5D=1&sub%5B%5D=2&sub%5B%5D=3

但是需要注意的是这是在<form>表单中没有type=file形内容的时候的提交方式, 如果表单中有二进制内容需要提交, 比如文件或者图片等, 那么就无法使用application/x-www-urlencoded方式, 需要转而使用下面会谈到的multipart/form-data方式.

# multipart/form-data

当需要提交二进制数据如文件或者图片时就需要使用这种multipart/form-data, 一个常见的提交内容结构如下:

POST http://www.example.com HTTP/1.1
Content-Type:multipart/form-data; boundary=----WebKitFormBoundaryrGKCBY7qhFd3TrwA

------WebKitFormBoundaryrGKCBY7qhFd3TrwA
Content-Disposition: form-data; name="text"

title
------WebKitFormBoundaryrGKCBY7qhFd3TrwA
Content-Disposition: form-data; name="file"; filename="chrome.png"
Content-Type: image/png

PNG ... content of chrome.png ...
------WebKitFormBoundaryrGKCBY7qhFd3TrwA--

请求头中的boundary代表将使用后面这一长串的字符串来分隔不同的字段, 消息主体里按照字段个数又分为多个结构类似的部分,每部分都是以--boundary开始,紧接着是内容描述信息,然后是回车,最后是字段具体内容(文本或二进制)。如果传输的是文件,还要包含文件名和文件类型信息。消息主体最后以--boundary--标示结束, 关于multipart/form-data的详细定义,请前往rfc1867查看。

application/x-www-urlencodedmultipart/form-data都是浏览器原生支持的,而且现阶段标准中原生<form>表单也只支持这两种方式(通过<form>元素的enctype属性指定,默认为 application/x-www-form-urlencoded。其实enctype还支持text/plain,不过用得非常少)。

下面提到的Content-Type属于随着技术的发展, 我们自定义出来的新的数据提交方式, 更为便捷.

# application/json

因为json格式数据的读写性非常好, 用的也极为广泛, 所以application/json这个请求头也用的越来越多, 这个请求头就是告诉服务器发送的数据是序列化后的json字符串, 现在在做的Vue项目中用到的Axios所使用的默认就是application/json, 这里有一个问题就是Axios全局设置Content-Typeapplication/x-www-urlencoded不生效, 需要在请求发出前拦截方法中修改配置才能生效, 不知道是不是bug, 如下:

function _interceptorRequest(config) {
  config.headers['Content-Type'] = 'application/x-www-form-urlencoded; charset=utf-8';
  return config
}

使用application/json方式发送的数据结构类似下面这个:

POST http://www.example.com HTTP/1.1
Content-Type: application/json;charset=utf-8

{"title":"test","sub":[1,2,3]}

json格式可以提交结构复杂的数据,在抓包工具或者调试中查看起来也很方便, 尤其适合RESEful的接口, 需要注意的是不论我们使用application/x-www-urlencoded还是application/json都要注意和服务器相配合, 因为毕竟我们发送的数据是希望服务器来正确接收和处理的, 如果客户端设置的Content-Type与服务端期望接收的Content-Type不一致就很有可能导致服务器无妨正常处理这个请求.

# text/xml

这是一种使用HTTP作为传输协议,XML作为编码方式的远程调用规范, 典型的XML-RPC请求如下:

POST http://www.example.com HTTP/1.1
Content-Type: text/xml

<?xml version="1.0"?>
<methodCall>
    <methodName>examples.getStateName</methodName>
    <params>
        <param>
            <value><i4>41</i4></value>
        </param>
    </params>
</methodCall>

XML-RPC协议简单、功能够用,各种语言的实现都有。它的使用也很广泛,如WordPressXML-RPCApi,搜索引擎的ping服务等等。JavaScript中,也有现成的库支持以这种方式进行数据交互,能很好的支持已有的 XML-RPC服务。

# 参考文章