Laravel解决预请求和跨域的问题

Posted 努力的九月

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Laravel解决预请求和跨域的问题相关的知识,希望对你有一定的参考价值。

场景描述

  最近在做公司的一个项目,我们在做这个小项目的时候,决定采用三端分离,数据库、服务器端、前端都分离。这样的分离是好的,但是我们遇到了一个问题,这个问题就是跨域问题和预请求问题。跨域的问题是因为我们将前端和后端放在两台服务器,数据之间的访问是通过RestApi进行的,这个时候的访问就涉及跨域的问题。还有一个问题就是预请求的问题,这个问题的引发就是在访问Api的时候要携带令牌,没有令牌是不能访问的。前端每次在访问接口的时候都要在请求头加上Token,加上token之后就出现了预请求的问题。为了解决预请求太搞脑子。下面我们就来说说预请求的问题。

CORS描述

一、CORS定义

  跨来源资源共享(CORS)是一份浏览器技术的规范,提供了 Web 服务从不同网域传来沙盒脚本的方法,以避开浏览器的同源策略,是 JSONP 模式的现代版。与 JSONP 不同,CORS 除了 GET 要求方法以外也支持其他的 HTTP 要求。用 CORS 可以让网页设计师用一般的 XMLHttpRequest,这种方式的错误处理比JSONP要来的好,JSONP对于 RESTful 的 API 来说,发送 POST/PUT/DELET 请求将成为问题,不利于接口的统一。但另一方面,JSONP 可以在不支持 CORS 的老旧浏览器上运作。不过现代的浏览器(IE10以上)基本都支持 CORS。

二、简单请求和非简单请求

  只要同时满足以下两大条件,就属于简单请求。否侧就是非简单请求。

(1) 请求方法是以下三种方法之一:

       HEAD
       GET
       POST
(2)HTTP的头信息不超出以下几种字段:

      Accept
      Accept-Language
      Content-Language
      Last-Event-ID
      Content-Type:只限于三个值application/x-www-form-urlencoded、multipart/form-data、text/plain

对于简单请求,浏览器直接发出CORS请求。具体来说,就是在头信息之中,增加一个Origin字段。

GET /cors HTTP/1.1
Origin: http://api.bob.com
Host: api.alice.com
Accept-Language: en-US
Connection: keep-alive
User-Agent: Mozilla/5.0...

非简单请求是那种对服务器有特殊要求的请求,比如请求方法是PUTDELETE,或者Content-Type字段的类型是application/json或者自定义请求头信息

非简单请求的CORS请求,会在正式通信之前,增加一次HTTP查询请求,称为"预检"请求(preflight)。

非简单请求两个重要的参数:

  1、Access-Control-Request-Method:该字段是必须的,用来列出浏览器的CORS请求会用到哪些HTTP方法。

  2、Access-Control-Request-Headers:该字段是一个逗号分隔的字符串,指定浏览器CORS请求会额外发送的头信息字段。

三、预检请求定义

  在 CORS 中,可以使用 OPTIONS 方法发起一个预检请求(一般都是浏览检测到请求跨域时,会自动发起),以检测实际请求是否可以被服务器所接受。预检请求报文中的 Access-Control-Request-Method 首部字段告知服务器实际请求所使用的 HTTP 方法;Access-Control-Request-Headers 首部字段告知服务器实际请求所携带的自定义首部字段。服务器基于从预检请求获得的信息来判断,是否接受接下来的实际请求。

预检请求的过程 

  

由上面图可以看出,解决预请求是靠服务器来解决。

请求失败的结果:如果失败请求头的Token就不能加上去

请求成功的结果

laravel怎么实现OPTIONS请求

  我在网上找了好多资料,看了之后都不能解决预检测的问题,因为项目的需要,需要在所有的Api接口的访问携带Token,这个时候我们注册中间件就要注册全局的,而不是路由的。

注意:我第一次在路由里面直接注册中间件,还是不能成功响应。通过调试发现Laravel预检测不能放在路由的中间件里面,如果放在哪就不生效。我们要放在全局中间件里面或者直接放在路由里面。

见代码

public function handle($request, Closure $next)
    {
        $response = $next($request);
        $response->header(\'Access-Control-Allow-Origin\', \'*\');
        $response->header(\'Access-Control-Allow-Headers\', \'Origin,No-Cache, X-Requested-With, If-Modified-Since, Pragma, Last-Modified, Cache-Control, Expires, Content-Type, X-E4M-With, token\');
        $response->header(\'Access-Control-Allow-Methods\', \'GET, POST, PATCH, PUT, OPTIONS\');
        $response->header(\'Access-Control-Allow-Credentials\', \'true\');
        //验证token是否
        $token = $request->header(\'token\');
        if(!$token){
            return Base::response(\'授权失败,请检查token\',0);
        }else{
            $res = (new Token())->verifyToken($token);
            if(!$res){
                return Base::response(\'token失效,请重新获取\',0);
            }
        }
        return $response;
    }

注册中间件

  protected $middleware = [
        \\Illuminate\\Foundation\\Http\\Middleware\\CheckForMaintenanceMode::class,
        \\Illuminate\\Foundation\\Http\\Middleware\\ValidatePostSize::class,
        \\App\\Http\\Middleware\\TrimStrings::class,
        \\Illuminate\\Foundation\\Http\\Middleware\\ConvertEmptyStringsToNull::class,
        \\App\\Http\\Middleware\\CorsHttp::class,
    ];

最后大功告成,希望可以帮助到大家。

  

以上是关于Laravel解决预请求和跨域的问题的主要内容,如果未能解决你的问题,请参考以下文章

使用 API(带有两个 jQuery 版本和跨域的页面)

AngularJS 和跨域 POST

同源和跨域

关于laravel框架的跨域请求/jsonp请求的理解

于laravel框架的跨域请求/jsonp请求的理解

iOS解决跨域问题