场景描述
最近在做公司的一个项目,我们在做这个小项目的时候,决定采用三端分离,数据库、服务器端、前端都分离。这样的分离是好的,但是我们遇到了一个问题,这个问题就是跨域问题和预请求问题。跨域的问题是因为我们将前端和后端放在两台服务器,数据之间的访问是通过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...
非简单请求是那种对服务器有特殊要求的请求,比如请求方法是PUT
或DELETE
,或者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 = [
IlluminateFoundationHttpMiddlewareCheckForMaintenanceMode::class,
IlluminateFoundationHttpMiddlewareValidatePostSize::class,
AppHttpMiddlewareTrimStrings::class,
IlluminateFoundationHttpMiddlewareConvertEmptyStringsToNull::class,
AppHttpMiddlewareCorsHttp::class,
];
最后大功告成,希望可以帮助到大家。