犯蠢日记(二)本地环境,A 项目通过 guzzle 请求 B 项目某个接口时,返回 404 响应
背景
- A 项目和 B 项目均是 Laravel + dingo/api 构建的项目
- A 项目通过 API_DOMAIN 配置了 API 接口的域名,B 项目没有
- 本地开发时两个项目在同一环境下,通过域名区分虚拟主机,域名用 hosts 指向 127.0.0.1
正文
由于某个原因,A 项目需要调用到 B 项目的某个接口,于是我使用 guzzle 去调用 B 项目的接口。
但是!!guzzle 却一直返回 404 响应!
通过本地使用 Postman 调用接口,我可以肯定 B 项目的该接口是肯定存在并且可以正常使用的。
这就非常奇怪了,为什么偏偏使用 guzzle 调用就会返回 404 响应?没有办法,只能根据 debug 信息里的 trace 一步步追查下去了。
追查过程非常曲折,就不在此赘述了,最终调试发现原因是路由的 host 验证没通过(\Illuminate\Routing\Matching\HostValidator
)。Request
对象里的 host 是 B 项目的域名,但是HostValidator
里的 host 却要求是 A 项目的 API_DOMAIN 域名,这就非常诡异了,初步怀疑是不是 Laravel 项目读取 .env 文件时会把里面的值都设置到环境变量里。
验证猜想,为了不读取 .env 文件,在 B 项目缓存配置:
$ php artisan config:cache
再次在 A 项目里使用 guzzle 调用 B 项目接口,接口返回成功!真是绝了,这特么都行,这 .env 文件加载看样子有坑啊。
可是本地调试终不能一直缓存配置啊,尝试寻找其他解决方案。
先清除 B 项目配置缓存,并使用 API_DOMAIN 配置 API 接口的域名,Postman 直接调用 B 项目接口,返回正常。
在 A 项目里使用 guzzle 调用 B 项目接口(对应修改了 url ),这次更牛逼了,返回虽然也是 404 响应,但不是 dingo/api 的 json 返回值,而是 Laravel 的 web 路由返回的 404 页面!这次直接进入不了 B 项目的 dingo/api 路由。。。在 B 项目缓存配置,guzzle 请求响应正常。
越来越有意思了,继续尝试,清除所有缓存,把 A 、B 项目都配置成 API_PREFIX 模式,Postman 直接调用 B 项目接口,返回正常,在 A 项目里使用 guzzle 调用 B 项目接口果然也返回正常,看样子真的是 .env 文件加载有坑啊。
衍生
想到 .env 文件加载有坑,突然就是一激灵,这不就意味着其实不仅仅是 API_DOMAIN 的值会有问题,所有 .env 文件里配置的变量其实全都会有问题!!比如数据库 DB_DATABASE !!哇,贼恐怖,立马写测试接口进行尝试,B 项目接口直接返回config('database.connections.mysql')
的值。
果然,在 A 项目里使用 guzzle 调用 B 项目接口时,B 项目接口里config('database.connections.mysql')
返回的是 A 项目的数据库配置。。。。在 B 项目缓存了配置之后,返回的就是正常的 B 项目数据库配置了。
哇,贼恐怖,简直无解了啊,这是逼我本地开发也要缓存 B 项目的配置了。
结论
折腾了这么久,最终只能妥协,缓存 B 项目的配置进行开发,希望过后我还会记得我在本地缓存过 B 项目的配置。。。
还有,线上环境也要注意部署在一台主机上的 Laravel 项目一定要缓存配置!!不然搞不好要出人命的 T_T
本作品采用《CC 协议》,转载必须注明作者和本文链接
如果 B 的接口为 api.test.com,B 的 env 配置应该为 API_DOMAIN=api.test.com ,前面不可以加http://,否则会404
@HI 这个没影响的,我这里出现 404 不是这个原因,并且其实加上了 http:// 也并不会报 404,亲测
@xinhuo 噢,那可能我和你遇到的情况不一样。当用户访问我的api地址的时候,我的中间件会根据用户访问的域名主体匹配env的api_domain,当我的env配置api_domain前面加了http的时候,对方在postman或接口调用我的地址加了http或不加http,laravel自带的获取域名主体会将http省略,所以导致匹配不到404,我遇到过的是这样