注意:简单请求和非简单请求都是在跨域的情况下来说的,也只有js ajax请求时才会有跨域的说法,form表单提交不存在跨域。

简单请求就是普通 HTML Form 也可以发出的请求,比如表单的 method 如果指定为 POST ,可以用 enctype 属性指定用什么方式对表单内容进行编码,合法的值就是你题目里提到的这三种。

非简单请求就是普通 HTML Form 无法实现的请求。比如 PUT 方法、需要其他的内容编码方式、自定义头之类的。

CORS跨域请求的简单请求和非简单请求条件

  1. 同时满足两大条件,就属于简单请求
    • 请求方式:GET、POST、HEAD(注:什么是HEAD请求?HEAD请求和GET本质是一样的,但是HEAD请求不含数据,只有HTTP头部信息)
    • HTTP头部信息不超过一下几种字段:无自定义头部字段、Accept、Accept-Language、Content-Language、Last-Event-ID、Content-Type(只有三个值application/x-www-form-urlencoded、multipart/form-data、text/plain)
  2. 非简单请求
    • 请求方式:PUT、DELETE
    • 自定义头部字段
    • 发送json格式数据
    • 正式通信之前,浏览器会先发送OPTION请求,进行预检,这一次的请求称为“预检请求”
    • 服务器成功响应预检请求后,才会发送真正的请求,并且携带真实数据

对于服务器来说,第一,许多服务器压根没打算给跨源用。当然你不给 CORS 响应头,浏览器也不会使用响应结果,但是请求本身可能已经造成了后果。所以最好是默认禁止跨源请求。

第二,要回答某个请求是否接受跨源,可能涉及额外的计算逻辑。这个逻辑可能很简单,比如一律say yes。也可能比较复杂,结果可能取决于哪个资源哪种操作来自哪个origin。对浏览器来说,就是某个资源是否允许跨源这么简单;对服务器来说,计算成本却可大可小。所以我们希望最好不用每次请求都让服务器劳神计算。

CORS-preflight 就是这样一种机制,浏览器先单独请求一次,询问服务器某个资源是否可以跨源,如果不允许的话就不发实际的请求。注意先许可再请求等于默认禁止了跨源请求。如果允许的话,浏览器会记住,然后发实际请求,且之后每次就都直接请求而不用再询问服务器否可以跨源了。于是,服务器想支持跨源,就只要针对 preflight 进行跨源许可计算。本身真正的响应代码则完全不管这个事情。并且因为 preflight 是许可式的,也就是说如果服务器不打算接受跨源,什么事情都不用做。

但是这机制只能限于非简单请求。在处理简单请求的时候,如果服务器不打算接受跨源请求,不能依赖 CORS-preflight 机制。因为不通过 CORS,普通表单也能发起简单请求,所以默认禁止跨源是做不到的。

既然如此,简单请求发 preflight 就没有意义了,就算发了服务器也省不了后续每次的计算,反而在一开始多了一次 preflight。

以上。

【补充:关于『向下兼容』】

把简单请求不需要preflight理解为『向下兼容』也不能说错。但严格来说,并不是『为了向下兼容』而不能发。理论上浏览器可以区别对待表单请求和非表单请求 —— 对传统的跨源表单提交不发preflight,从而保持兼容,只对非表单跨源请求发preflight。

但这样做并没有什么好处,反而把事情搞复杂了。比如本来你可以直接用脚本发跨源普通请求,尽管(在服务器默认没有跨源处理的情况下)你无法得到响应结果,但是你的需求可能只是发送无需返回,比如打个日志。但现在如果服务器不理解preflight你就干不了这个事情了。

而且如果真的这样做,服务器就变成了默认允许跨源表单,如果想控制跨源,还是得(跟原本一样)直接在响应处理中执行跨源计算逻辑;另一方面服务器又需要增加对preflight请求的响应支持,执行类似的跨源计算逻辑以控制来自非表单的相同跨源请求。服务器通常没有区分表单/非表单差异的需求,这样搞纯粹是折腾服务器端工程师。

所以简单请求不发preflight不是因为不能兼容,而是因为兼容的前提下发preflight对绝大多数服务器应用来说没有意义,反而把问题搞复杂了。

发表评论

电子邮件地址不会被公开。 必填项已用*标注

Scroll Up