人在江湖走,哪有不湿鞋?
经常做api开发的的时候,总会遇到跨域的情况,特别是前后端完全分离的情况,为什么会这样呢?
出于安全性的原因,浏览器会限制 Script 中的跨域请求。由于 XMLHttpRequest 遵循同源策略,所有使用 XMLHttpRequest 构造 HTTP 请求的应用只能访问自己的域名,如果需要构造跨域的请求,那么开发者需要配合浏览器做出一些允许跨域的配置。
解决办法有很多,主要说下两种:
方法之一,修改nginx
修改web服务器的配置允许跨域,一劳永逸,以nginx为例:
在站点配置节点下添加:
server
{
listen 80;
index index.html index.htm index.php;
root /www/public/;
add_header Access-Control-Allow-Origin *;
add_header Access-Control-Allow-Headers "x-requested-with,Authorization,Accept, Origin, X-Requested-With, Content-Type, Last-Modified";
add_header Access-Control-Allow-Methods GET,POST;
add_header Access-Control-Max-Age 1728000;
# ....
}
方法之二,在php上做手脚
虽然修改nginx配置是挺方便,让你懒到不用改一行代码,但:
不够灵活,不能针对某个接口去允许跨域,只能全局设置,不够安全;
条件不一定允许,很多时候不一定拥有nignx的修改权限。
这里说下laravel框架里设置跨域:
首先,您需要一个中间件:
<?php
namespace App\Http\Middleware;
use Closure;
class EnableCrossRequest
{
/**
* Handle an incoming request.
*
* @param \Illuminate\Http\Request $request
* @param \Closure $next
* @return mixed
*/
public function handle($request, Closure $next)
{
$origin = $request->server('HTTP_ORIGIN') ? $request->server('HTTP_ORIGIN') : '';
$originHost = parse_url($origin, PHP_URL_HOST);
$originPort = parse_url($origin, PHP_URL_PORT);
$originHost .= $originPort ? ':' . $originPort : '';
// 允许跨域的域名 可以加在配置里
$allowOriginHost = [
'test.com',
];
$response = $next($request);
if (in_array($originHost, $allowOriginHost)) {
$response->header('Access-Control-Allow-Origin', $origin);
$response->header('Access-Control-Allow-Headers', 'Origin, Content-Type, Cookie, X-CSRF-TOKEN, Accept, Authorization, X-XSRF-TOKEN, Last-Modified');
$response->header('Access-Control-Expose-Headers', 'Authorization, authenticated');
$response->header('Access-Control-Allow-Methods', 'GET, POST, PATCH, PUT, OPTIONS');
$response->header('Access-Control-Allow-Credentials', 'true');
}
return $response;
}
}
然后注册中间件,app/Http/Kernel.php
protected $middlewareGroups = [
// ....
'api.cross' => [
\App\Http\Middleware\EnableCrossRequest::class,
],
// ....
];
然后,在需要跨域的路由上加上该中间件
Route::group([
'prefix' => 'api',
'middleware' => ['api.cross'],
], function () {
// 路由
});
解决axios跨域时,发送post请求变options的问题
跨域遇到复杂请求时, 会先发送options去试探是否支持跨域, 所以有两次请求完全正常,但会导致程序重复处理,所以可以通过以下方法屏蔽options请求
Route::options('/{all}', function () {
return response('');
})->where(['all' => '([a-zA-Z0-9-]|_|/)+']);