「新轮子」 PHP CORS (Cross-origin resource sharing),解决 PHP 项目程序设置跨域需求 - V2EX
V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
medz
V2EX    PHP

「新轮子」 PHP CORS (Cross-origin resource sharing),解决 PHP 项目程序设置跨域需求

  •  
  •   medz 2018-03-15 23:28:08 +08:00 3021 次点击
    这是一个创建于 2817 天前的主题,其中的信息可能已经有所发展或是发生改变。

    file

    是的,可能了解 Laravel 的都知道,在 Laravel 中简单的设置跨域,当然是选择 barryvdh/laravel-cors 这个包。

    起因

    是的,我确实是在重复造轮子,barryvdh/laravel-cors 包实现很优秀了,但是它也有很多问题。而我的新轮子 medz/cors 也并不是专门解决它的问题的,而是顺便解决而已。medz/cors 包的灵感来自公司项目,公司的项目使用 Laravel 框架进行开发,前端使用完全分离的方式。所以需要设置跨域,而传统 nginx 设置跨域虽然可信,但是程序设置跨域优点不言而喻(可以设置多个允许跨域域名等,动态处理信息)。

    概述

    medz/cors 这个包,支持在所有 PHP 的项目中使用。你只需要一些简单的配置即可。而这里主要先讲在 Laravel 中的应用,因为目前内置支持暂时对 Laravel 做了很好的专属处理。

    打算友好支持如下

    • [x] PHP Native coding.
    • [x] Laravel >= 5.5
    • [x] PSR-7
    • [ ] Symfony
    • [ ] Yii2
    • [ ] Slim Framework

    安装

    感谢 Composer 为社区带来的改变,现在我们都喜欢它,所以你只需要在你的项目中使用:

    composer require medz/cors 

    好了,你已经安装好了。如果你是 Laravel,那么,现在你已经为你的程序加上了跨域

    发布配置文件

    运行:

    php artisan vendor::publish --force --class="\Medz\Cors\Laravel\Probiders\LaravelServiceProvider" 

    有兴趣看配置的可以看一下 confih/cors.php 的配置。但是下面会专门讲配置的。

    配置

    为了方便一些配置,个别 boolintstring 类型的配置可以使用 .env 进行环境变量设置:

    | Key | 描述 | |----|----| | CORS_ALLOW_CREDENTIAILS | bool, 设置 Access-Control-Allow-Credentialstrue 还是 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.phpprotected $middleware 所设置的中间件,所以针对 Laravel medz/cors 包自动在这里添加了 Medz\Cors\Laravel\Middleware\Cors 中间件。你发现,这个中间件如果你进行了一些配置,他是不会执行其他任何处理的。

    路由组模式

    这是解决 barryvdh/laravel-cors 的痛点之一吧,因为 barryvdh/laravel-cors 会给所有的路由都加上跨域信息设置,这真的不是在实际中需要的,我们想要的是特定的路由组,或者特定的路由才支持跨域,其他的路由无法进行跨域请求。所哟针对 Laravel 的开发中,有了这个模式。

    而组模式需要用到的中间件叫做 Medz\Cors\Laravel\Middleware\ShouldGroup,为了方便使用,你可以在 app/Http/Kernel.phpprotected $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 默认的两个路由中间件组有 webapi 两个组,首先 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。

    6 条回复    2018-03-16 11:34:29 +08:00
    MeteorCat
        1
    MeteorCat  
       2018-03-16 08:00:36 +08:00 via Android
    跨域不是在 nginx 设置就行了吗?
    medz
        2
    medz  
    OP
       2018-03-16 09:34:40 +08:00
    @MeteorCat 是的,但是 Nginx 设置有很多非动态局限性,例如设置 `Allow-Header` 为 `*` 等!而且如果是需要支持多个 `Origin` 也是问题,程序设置的话,可以直接控制。
    medz
        3
    medz  
    OP
       2018-03-16 09:35:07 +08:00
    对了,发现 V2EX 居然不支持 Markdown 的 Table 语法
    sunmonster
        5
    sunmonster  
       2018-03-16 11:12:16 +08:00
    写个 middleware,两行代码能够解决的问题还要弄个包,向 nodejs 看齐吗?
    medz
        6
    medz  
    OP
       2018-03-16 11:34:29 +08:00
    @sunmonster 包的意义不在于此,并不是所有的 PHP coder 都能处理好这件事情。
    关于     帮助文档     自助推广系统     博客     API     FAQ     Solana     1071 人在线   最高记录 6679       Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 32ms UTC 17:31 PVG 01:31 LAX 09:31 JFK 12:31
    Do have faith in what you're doing.
    ubao msn snddm index pchome yahoo rakuten mypaper meadowduck bidyahoo youbao zxmzxm asda bnvcg cvbfg dfscv mmhjk xxddc yybgb zznbn ccubao uaitu acv GXCV ET GDG YH FG BCVB FJFH CBRE CBC GDG ET54 WRWR RWER WREW WRWER RWER SDG EW SF DSFSF fbbs ubao fhd dfg ewr dg df ewwr ewwr et ruyut utut dfg fgd gdfgt etg dfgt dfgd ert4 gd fgg wr 235 wer3 we vsdf sdf gdf ert xcv sdf rwer hfd dfg cvb rwf afb dfh jgh bmn lgh rty gfds cxv xcv xcs vdas fdf fgd cv sdf tert sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf shasha9178 shasha9178 shasha9178 shasha9178 shasha9178 liflif2 liflif2 liflif2 liflif2 liflif2 liblib3 liblib3 liblib3 liblib3 liblib3 zhazha444 zhazha444 zhazha444 zhazha444 zhazha444 dende5 dende denden denden2 denden21 fenfen9 fenf619 fen619 fenfe9 fe619 sdf sdf sdf sdf sdf zhazh90 zhazh0 zhaa50 zha90 zh590 zho zhoz zhozh zhozho zhozho2 lislis lls95 lili95 lils5 liss9 sdf0ty987 sdft876 sdft9876 sdf09876 sd0t9876 sdf0ty98 sdf0976 sdf0ty986 sdf0ty96 sdf0t76 sdf0876 df0ty98 sf0t876 sd0ty76 sdy76 sdf76 sdf0t76 sdf0ty9 sdf0ty98 sdf0ty987 sdf0ty98 sdf6676 sdf876 sd876 sd876 sdf6 sdf6 sdf9876 sdf0t sdf06 sdf0ty9776 sdf0ty9776 sdf0ty76 sdf8876 sdf0t sd6 sdf06 s688876 sd688 sdf86