这篇文章是在学习content-type的时候联想到的,为什么简单请求/非简单请求要区分不同的content-type,非常具体的就是那三种?依据是什么?
如何区分简单/非简单请求?
浏览器对跨域请求中的简单请求和非简单请求的处理是不一样的:
简单请求:
- 方法:HEAD GET POST
- 请求头不超过以下字段
Accept、Accept-Language、Content-Language、Last-Event-ID、DPR、Downlink、Save-Data、Viewport-Width、Width、Content-Type:只限于三个值application/x-www-form-urlencoded、multipart/form-data、text/plain
非简单请求
不同时满足上述条件的请求就是非简单请求。浏览器对于非简单请求会进行一次预检请求。预检请求会将本次请求的请求方式、请求头和origin发送到服务器,询问服务器是否允许发送此次请求。
为什么要区分简单请求和非简单请求?
对于简单请求和非简单请求的这些限制有许多疑问,开始对于区分简单/非简单请求的原因理解如下:
服务器对于跨域请求通常会返回cors相关的响应头来告知浏览器是否允许此次跨域操作,但是对于一些直接能够修改或者新建服务器资源的请求,比如PUT DELETE 等请求,在没有返回响应之前就会对服务器资源造成损害。针对这种请求,浏览器需要首先进行一次预检请求,询问服务器是否允许此次请求,以达到保护服务器资源的目的。
后来,我开始纠结,content-type为multipart/form-data的post请求不是也会修改服务器资源吗?为什么将它划分为简单请求?
看了一篇文章,CORS 为什么要区分『简单请求』和『预检请求』?,发现事情可能没那么简单。
- 划分简单/预检跨域请求的依据
传统的HTML form在不依赖脚本的情况下,通过POST、GET、HEAD
请求,content-type
可以设置text/plain、multipart/form-data和application/x-www-form-urlencoded
三种,来发送跨域请求(url)。简单来说,浏览器对这些形式下的跨域请求做不了任何限制。
其他形式的请求是普通的form无法实现的,需要依赖脚本去添加一些自定义请求头或者form本身无法实现的方法(PUT等)
根据这个来区分简单/预检请求。
- 为什么要对非简单跨域请求做预检?
原因有以下几点:
-
减少非简单跨域请求对服务器的影响(开始时就提到,服务器有时候不想理睬跨域请求),比如PUT、DELETE请求可以直接新建或者修改、删除服务器中的资源。预检请求可以防止该情况发生。
-
减少服务器对于是否跨域的计算量
对于非简单请求的跨域请求,服务器对于是否跨域的计算是在预检请求上,如果预检请求通过之后,正式请求都不用再次计算。而且一次预检请求通过后,之后的每次请求都只会发正式请求。节约了很多服务端的计算量。
- 为什么不对简单的跨域请求做预检?
原因有以下几点:
- 一开始就提到,form能实现的简单跨域请求,浏览器做不了任何的限制。
- 没必要对简单请求做预检。比如,一些post请求只是想打个日志,并不需要服务器的响应,但是如果加预检请求,预检请求不通过就做不了这件事。还有一些GET请求、HEAD请求只是想获取资源,并不会修改资源,在不获取响应的时候并不会对服务器造成影响。在这种情况下,加预检请求,只会增加服务器的负担
为什么不区分表单跨域简单请求和非表单跨域简单请求?
最后,杠精(比如我)就会问了,既然一开始是因为form来区分两者的,那不如就按照表单跨域来区分一下:简单表单跨域请求和简单非表单跨域请求,绕晕了…,对于非表单简单跨域请求实行预检请求,怎么样,是不是完美无缺(微笑)…
原因如下:
-
这样划分之后,相当于服务器默认允许表单跨域请求。
-
对于其他非表单跨域简单请求进行预检也无必要,他们并不会对服务器造成不可想象的影响,还会麻烦服务器又要进行额外的预检响应。
-
服务器还要区分表单/非表单跨域请求,弄啥嘞!!