HTTP之 options请求

options请求

        在前端项目中,前端通过AJAX方式访问后端的REST接口时,发现两条请求记录,一条请求的Request Method为Options,另一条请求的Reuest Method为Post。想要解决这个疑惑还得从以下3个概念说起。

        

CORS(跨域资源共享)

        CORS是一种网络浏览器的技术规范,它为Web服务器定义了一种方式,允许网页从不同的域访问其资源。而这种访问是被同源策略所禁止的。CORS系统定义了一种浏览器和服务器交互的方式来确定是否允许跨域请求。 

使用CORS的方式非常简单,但是需要同时对前端和服务器端做相应处理。

        1、  前端

            客户端使用XmlHttpRequest发起Ajax请求,当前绝大部分浏览器已经支持CORS方式,且主流浏览器均提供了对跨域资源共享的支持。

        2、  服务器端

            如果服务器端未做任何配置,则前端发起Ajax请求后,会得到CORS Access Deny,即跨域访问被拒绝。


官方定义:

    1、OPTIONS方法是用于请求获得由Request-URI标识的资源在请求/响应的通信过程中可以使用的功能选项。通过这个方法,客户端可以在采取具体资源请求之前,决定对该资源采取何种必要措施,或者了解服务器的性能。

    2、该请求方法的响应不能缓存。

    3、如果这个OPTIONS请求包含一个正文(有Content-Length或Transfer-Encoding存在),则必须有Content-Type来指定媒体类型。虽然规范里没有定义这种正文的用法,但是HTTP将来的扩展可能会用它来查询服务器上更详细的信息。不支持该扩展的服务器可以忽略该请求正文。

    4、如果该URI是一个星号(“*”),OPTIONS请求将试图应用于服务器,而不是某个指定资源。由于服务器的通信选项通常依赖于资源,所以此“*”请求只能作为“ping”或者“no-op”方法;或者用来测试服务器的性能。例如,用来测试HTTP/1.1代理。

    5、如果该URI不是星号,则只能用来获取该资源通信中可用的选项。

    6、得到的200响应应该包含一个头域,指明服务器实现的和适用于该资源的可选特征(如:Allow),可能还包括该规范尚未定义的扩展。如果有响应正文,则应包含关于通信选项的信息。本规范没有定义该正文格式,但可能在HTTO将来的扩展中定义。可以利用内容协商来选择合适的响应格式。如果没有响应正文,响应必须包含Content-Length,并且值为“0”。

    7、请求头的Max-Forwards用来请求特定代理。当代理收到一个允许URI转发的OPTIONS请求,则检查Max-Forwards。如果Max-Forwards值为0,则不能转发该消息;相反,代理会将自己的通信选项去响应。如果Max-Forwards是正整数,代理转发请求的时候会将该值减1。如果请求中没有Max-Forwards,转发的请求也不会有。



Preflighted Requests(预检请求):

Preflighted Requests是CORS中一种透明服务器验证机制。预检请求首先需要向另外一个域名的资源发送一个 HTTP OPTIONS 请求头,其目的就是为了判断实际发送的请求是否是安全的。(HTTP请求方法并不是只有GET和POST,只是最常用的。据RFC2616标准(现行的HTTP/1.1)得知,通常有以下8种方法:OPTIONS、GET、HEAD、POST、PUT、DELETE、TRACE和CONNECT。)

下面的2种情况需要进行预检:

1、简单请求:比如使用Content-Type 为 application/xml 或 text/xml请求;

2、非简单请求:比如header中设置自定义头,比如 X-JSON、X-MENGXIANHUI 等。


两种请求讲解:

        浏览器将CORS请求分为两类:简单请求(simple request)和非简单请求(not-simple-request),简单请求浏览器不会预检,而非简单请求会预检。这两种方式怎么区分?

同时满足下列三大条件,就属于简单请求,否则属于非简单请求

    1.请求方式只能是:GET、POST、HEAD

    2.HTTP请求头限制这几种字段:Accept、Accept-Language、Content-Language、Content-Type、Last-Event-ID

    3.Content-type只能取:application/x-www-form-urlencoded、multipart/form-data、text/plain

对于简单请求,浏览器直接请求,会在请求头信息中,增加一个origin字段,来说明本次请求来自哪个源(协议+域名+端口)。服务器根据这个值,来决定是否同意该请求,服务器返回的响应会多几个头信息字段,如图所示:上面的头信息中,三个与CORS请求相关,都是以Access-Control-开头。

    1.Access-Control-Allow-Origin:该字段是必须的,* 表示接受任意域名的请求,还可以指定域名

    2.Access-Control-Allow-Credentials:该字段可选,是个布尔值,表示是否可以携带cookie,(注意:如果Access-Control-Allow-Origin字段设置*,此字段设为true无效)

    3.Access-Control-Allow-Headers:该字段可选,里面可以获取Cache-Control、Content-Type、Expires等,如果想要拿到其他字段,就可以在这个字段中指定。比如图中指定的GUAZISSO

非简单请求是对那种对服务器有特殊要求的请求,比如请求方式是PUT或者DELETE,或者Content-Type字段类型是application/json。都会在正式通信之前,增加一次HTTP请求,称之为预检。浏览器会先询问服务器,当前网页所在域名是否在服务器的许可名单之中,服务器允许之后,浏览器会发出正式的XMLHttpRequest请求,否则会报错。(备注:之前碰到预检请求后端没有通过,就不会发正式请求)排除Options


OPTIONS请求方法的主要用途有两个:
    1、获取服务器支持的HTTP请求方法;也是黑客经常使用的方法。
    2、用来检查服务器的性能。例如:AJAX进行跨域请求时的预检,需要向另外一个域名的资源发送一个HTTP OPTIONS请求头,用以判断实际发送的请求是否安全。



解决方案:

后端在进行跨域请求允许的时候需要做一个OPTIONS 方式请求判定,如果允许跨域,则设置允许跨域的规则并且响应一个200的Response(有些框架是需要单独设置options路由的,所以需要注意你的路由是否允许options请求,如果不允许则需要添加对应的路由),以下以PHP为例:注意当自定义header时,需要在Access-Control-Allow-Headers中加上自定义的KEY,如果你不追求安全性,这该值可以是*代表所有

         header('Content-Type: multipart/form-data;charset=utf-8');
         header('Access-Control-Allow-Origin:*'); // *代表允许任何网址请求
         header('Access-Control-Allow-Methods:POST,GET,OPTIONS,DELETE,PUT'); // 允许请求的类型
         header('Access-Control-Allow-Credentials: true'); // 设置是否允许发送 cookies
         header('Access-Control-Allow-Headers: Content-Type,Content-Length,Accept-Encoding,X-Requested-with, Origin, SC-API-TOKEN');
         if ($request->method() == "OPTIONS") {
           die(200);//直接关闭请求,并将响应码设置为200,表示正常响应
         }


阅读数:260
如有疑问请与我联系:点击与我联系