是的,可能了解 Laravel 的都知道,在 Laravel 中简单的设置跨域,当然是选择 barryvdh/laravel-cors
这个包。
是的,我确实是在重复造轮子,barryvdh/laravel-cors
包实现很优秀了,但是它也有很多问题。而我的新轮子 medz/cors
也并不是专门解决它的问题的,而是顺便解决而已。medz/cors
包的灵感来自公司项目,公司的项目使用 Laravel 框架进行开发,前端使用完全分离的方式。所以需要设置跨域,而传统 nginx 设置跨域虽然可信,但是程序设置跨域优点不言而喻(可以设置多个允许跨域域名等,动态处理信息)。
medz/cors
这个包,支持在所有 PHP 的项目中使用。你只需要一些简单的配置即可。而这里主要先讲在 Laravel 中的应用,因为目前内置支持暂时对 Laravel 做了很好的专属处理。
打算友好支持如下
5.5
感谢 Composer
为社区带来的改变,现在我们都喜欢它,所以你只需要在你的项目中使用:
composer require medz/cors
好了,你已经安装好了。如果你是 Laravel,那么,现在你已经为你的程序加上了跨域
运行:
php artisan vendor::publish --force --class="\Medz\Cors\Laravel\Probiders\LaravelServiceProvider"
有兴趣看配置的可以看一下 confih/cors.php
的配置。但是下面会专门讲配置的。
为了方便一些配置,个别 bool
、int
和 string
类型的配置可以使用 .env
进行环境变量设置:
| Key | 描述 |
|----|----|
| CORS_ALLOW_CREDENTIAILS | bool
, 设置 Access-Control-Allow-Credentials
是 true
还是 false
|
| CORS_ACCESS_CONTROL_MAX_AGE | int
, 设置 Access-Control-Max-Age
的值,默认是 0
,即关闭 |
| CORS_LARAVEL_ALLOW_ROUTE_PERFIX | string
,路由匹配模式的匹配规则,因为使用 $request->is
进行检查,所以你可以参考 请求,默认是 *
,代表所有路由。|
| CORS_LARAVEL_ROUTE_GROUP_MODE | bool
, 是否启用「单一路由中间件」或者「路由中间件组」模式,默认是不启用。 |
然后所有的配置如下(建议看一下 config/cors.php
文件每个配置的注视):
| Key | 描述 |
|----|----|
| allow-credentiails
| 参考 CORS_ALLOW_CREDENTIAILS
配置 |
| allow-headers
| 列出允许的 header 字段列表,默认是 ['*']
代表全部,你可以设置例如「'[Content-Type', 'X-Requested-With']
」只允许上面的两个 header 字段,具体根据你的项目而定。一旦出现 *
成员,代表允许全部。 |
| expose-headers
| 列出了哪些首部可以作为响应的一部分暴露给外部。默认为 []
。 |
| origins
| 列出允许跨域的域名,默认是 ['*']
, 代表设置为 *
(只要出现 *
在列中都会返回 *
),例如设置 ['https://laravel-china.org']
,可以设置多个,程序会自动处理。|
| methods
| 列出允许跨域请求的方法列表,默认是 ['*']
代表所有方法。 |
| max-age
| 预检请求返回的结果是否可以被缓存,默认是 0
即代表不可以被缓存,单位是「秒」你可以设置允许预检请求结果缓存多久。 |
| laravel.allow-route-perfix
| 参考 CORS_LARAVEL_ALLOW_ROUTE_PERFIX
|
| laravel.route-group-mode
| 参考 CORS_LARAVEL_ROUTE_GROUP_MODE
|
其实,你的 Laravel 程序依赖了这个medz/cors
你就不需要在进行任何代码修改,就可以直接使用了。因为 Laravel 有一个申请的特点吧,就是发起 OPTIONS
预检请求的时候,执行的中间件只有「全局中间件」,即 app/Http/Kernel.php
中 protected $middleware
所设置的中间件,所以针对 Laravel medz/cors
包自动在这里添加了 Medz\Cors\Laravel\Middleware\Cors
中间件。你发现,这个中间件如果你进行了一些配置,他是不会执行其他任何处理的。
这是解决 barryvdh/laravel-cors
的痛点之一吧,因为 barryvdh/laravel-cors
会给所有的路由都加上跨域信息设置,这真的不是在实际中需要的,我们想要的是特定的路由组,或者特定的路由才支持跨域,其他的路由无法进行跨域请求。所哟针对 Laravel 的开发中,有了这个模式。
而组模式需要用到的中间件叫做 Medz\Cors\Laravel\Middleware\ShouldGroup
,为了方便使用,你可以在 app/Http/Kernel.php
的 protected $routeMiddleware
中给它取一个简短好记忆的避免,例如:
protected $routeMiddleware = [
'cors-should' => \Medz\Cors\Laravel\Middleware\ShouldGroup::class,
];
我给它去了一个名字叫做 cors-should
,现在,你可以在特定的路由中设置允许跨域:
Route::middleware('cors-should')->get('test-cors', function () {});
当然,你也可以对一个路由组使用,和上面单个路由一样,参考 Laravel 的 Route 文档吧。
你还可以直接设置到中间件组,这样,只允许某写中间件组的 URI 允许跨域,例如 Laravel 默认的两个路由中间件组有 web
和 api
两个组,首先 web
组肯定不是我们想要跨域的,而 api
我们可能是完全的前后端分离开发,前端程序不在当前 API 服务器的域上,产生了跨域,我们可以直接给 api
组设置允许 api
组跨域:
protected $middlewareGroups = [
/// ...
'web' => [
// ...
],
'api' => [
\Medz\Cors\Laravel\Middleware\ShouldGroup::class,
// ...
],
];
当然,你给 Medz\Cors\Laravel\Middleware\ShouldGroup::class
设置了路由中间价别名的话(例如:chors-should
),你可以:
protected $middlewareGroups = [
/// ...
'web' => [
// ...
],
'api' => [
'cors-should',
// ...
],
];
注意,路由组模式是会和「路由匹配模式」混合使用的
在「配置中」讲到了 ENV
常量 CORS_LARAVEL_ALLOW_ROUTE_PERFIX
或者配置文件中的 laravel.allow-route-perfix
是的,这就是路由匹配模式,默认是 *
即匹配所有。
路由匹配模式的匹配方法是使用 Laravel 中 Illumante\Http\Request::is
方法,文档请参考 Laravel 请求文档,所以设置的规则和 Laravel 请求问中 is
方法要求一致。例如,我们希望 api
前缀的路由才开启跨域:那么设置为:
api/*
即可,⚠️注意,路由匹配模式就像在 「路由组」模式提到了,会和「路由组」模式进行一起匹配。以 API 为了,我们使用「路由组模式」只允许 api
中间件组的路由允许跨域,同时我们设置 api/v2/*
的「路由匹配模式」规则,那么,允许跨于的只有 api/v2/*
的路由可以,例如 api/v1/*
也是 api
中间件组的路由,虽然组模式匹配上了,但是「路由匹配模式」的规则没有匹配上,所以 api/v1/*
的 路由是没有跨域允许信息的。
其实 medz/cors
不只是在 Laravel 项目中使用,你可以用于任何 PHP 程序中,只是包只会内置几个主流框架的预设支持代码而已,你可以用 Array
模式来让所有的 PHP 程序都支持。
因为一些语法原因,这个包只能用在 PHP >= 7.0
的版本之上,并且暂时不会进行语法妥协。
GitHub 地址: https://github.com/medz/cors
嗯,新轮子,求一波 🌟Star。
1
MeteorCat 2018-03-16 08:00:36 +08:00 via Android
跨域不是在 nginx 设置就行了吗?
|
2
medz OP @MeteorCat 是的,但是 Nginx 设置有很多非动态局限性,例如设置 `Allow-Header` 为 `*` 等!而且如果是需要支持多个 `Origin` 也是问题,程序设置的话,可以直接控制。
|
3
medz OP 对了,发现 V2EX 居然不支持 Markdown 的 Table 语法
|
5
sunmonster 2018-03-16 11:12:16 +08:00
写个 middleware,两行代码能够解决的问题还要弄个包,向 nodejs 看齐吗?
|
6
medz OP @sunmonster 包的意义不在于此,并不是所有的 PHP coder 都能处理好这件事情。
|