基础 - 请求的ContentType

  • 作者:KK

  • 发表日期:2017.11.29


默认的ContentType

请求报文里的Content-Type字段就是描述了请求报文的内容是怎样一种格式类型,比如下面一个登录POST请求报文,Content-Type 描述了报文体是 UrlEncode格式

POST http://localhost/login HTTP/1.1
Host: localhost
Connection: keep-alive
Accept: application/json, text/javascript, */*; q=0.01
X-Requested-With: XMLHttpRequest
User-Agent: Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/53.0.2785.104 Safari/537.36 Core/1.53.4033.400
Accept-Encoding: gzip, deflate, sdch
Accept-Language: zh-CN,zh;q=0.8
Content-Type: application/x-www-form-urlencoded
Content-Length: 33
If-None-Match: W/"3c-YDb/aDwHTHsnfnQxdPkCcz1vPbU"

email=xxx@yyy.com&password=123456

浏览器的表单同步提交默认就是以这种 Content-Type 来组织报文体的了(包括jQuery的ajax也是),但实际上我们可以自己另外报文的形态。


自定义ContentType

这个报头可能会影响服务端对请求报文的解析,比如换成application/json,执行以下JS代码可以构造以下 Content-Type 的请求报文:

$.ajax({
	url : 'http://localhost/login',
	type : 'post',
	dataType : 'application/json',
	data : JSON.stringify({
		email : 'xxx@yyy.com',
		password : '123456',
	})
});

请求发出后我抓到的报文是如下,注意 Content-Type 报头和报文体:

POST http://localhost/login HTTP/1.1
Host: localhost
Connection: keep-alive
Accept: application/json, text/javascript, */*; q=0.01
X-Requested-With: XMLHttpRequest
User-Agent: Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/53.0.2785.104 Safari/537.36 Core/1.53.4033.400
Accept-Encoding: gzip, deflate, sdch
Accept-Language: zh-CN,zh;q=0.8
Content-Type: application/json
Content-Length: 43
If-None-Match: W/"3c-YDb/aDwHTHsnfnQxdPkCcz1vPbU"

{"email":"xxx@yyy.com","password":"123456"}

自定义的通常要自己解析

比如在PHP脚本里,如果请求报文采用了json作为报文体的格式,那么默认情况下$_POST数组是空的,以前有内容是因为底层将 UrlEncode格式 的报文预先解析成了数组并存到$_POST变量中,一旦报文不是这个格式,那解析器就无法解析了,此时就要靠我们自己用代码解析:

if($_SERVER['CONTENT_TYPE'] !== 'application/json'){
	return;
}

$postString = file_get_contents('php://input');  //读取请求报文数据
$_POST = json_decode($postString, 1);

if(!$errorCode = json_last_error()){
	print_r($_POST);
	
}else{
	$error = '';
	if($errorCode == JSON_ERROR_NONE){
		$error = '没有错误发生';
		
	}elseif($errorCode == JSON_ERROR_DEPTH){
		$error = '到达了最大堆栈深度';
		
	}elseif($errorCode == JSON_ERROR_STATE_MISMATCH){
		$error = '无效或异常的 JSON';
		
	}elseif($errorCode == JSON_ERROR_CTRL_CHAR){
		$error = '控制字符错误,可能是编码不对';
		
	}elseif($errorCode == JSON_ERROR_SYNTAX){
		$error = '语法错误';
		
	}elseif($errorCode == JSON_ERROR_UTF8){
		$error = '异常的 UTF-8 字符,也许是因为不正确的编码。'; //最经常是遇到这个错误
		
	}elseif($errorCode == JSON_ERROR_RECURSION){
		$error = '被encode的数组存在互相引用的值';
		
	}elseif($errorCode == JSON_ERROR_INF_OR_NAN){
		$error = '被encode的数组存在NAN或INF的值';
		
	}elseif($errorCode == JSON_ERROR_UNSUPPORTED_TYPE){
		$error = '所传参数变量类型无法进行encode';
	}
	
	echo $error;
}

因此当收不到请求参数的时候,可以抓包看看Content-Type是什么,而服务端是否又部署了对应的解析代码


越来越多的新平台API都使用json报文类型

因为 UrlEncode 有如下缺点:

  1. 全部值都是字符串,服务端校验转换比较麻烦

  2. 表达深层次结构关系时,路径描述符占用了太多字节导致报文体偏大,浪费不必要的带宽

  3. 写文档的时候我们虽然用结构化嵌套式的结构描述了多层参数,但是有许多程序员不擅于将这些描述转换成 UrlEncode 的编码实现

而 json 可以表达 数字、字符串、boolean、null、数组、字典,对于API设计提供了足够丰富的支持,谷歌的前端框架 Angular 也是默认发送 json 格式的报文


甚至有用xml的

比如 SOAP协议 通讯时就是使用application/xml作为报文格式,微信公众号的接口也是这样,用 XML 会有更多考量,有更强大的支持(银行业、海运、航空等重要领域通讯都会采用 XML),但我们平时接触的项目其实一般 json 就足够了,选择自己合适的就好,最好将报文构造器和解析器封装好,并提供切换支持,要换啥就啥。