Laravel 5.3 API route 无法访问且方法未执行
结论
如果路由定义在 Laravel 5.3 的 routes/api.php 中,Laravel 默认会为它添加 /api 前缀。因此,实际访问地址应为:
http://anaketesting.tk/api/product-service/payment-notification
原地址少了 /api:
http://anaketesting.tk/product-service/payment-notification
这个请求通常匹配不到对应的 API 路由,所以控制器的 notification() 方法不会执行,方法中的日志和文件写入也不会发生。
正确的路由定义
在 routes/api.php 中定义路由:
<?php
Route::get('product-service/payment-notification', [
'as' => 'productService.notification',
'uses' => 'ProductServiceController@notification',
]);
访问地址为:
GET /api/product-service/payment-notification
如果不想让 URL 包含 /api,可以把路由放到 routes/web.php,也可以修改 app/Providers/RouteServiceProvider.php 中 API 路由组的前缀。不过,修改全局前缀会影响其他接口,通常不建议为了一个接口这样处理。
先确认 Laravel 实际注册了什么路由
在项目目录运行:
php artisan route:list
查看输出中的 URI、请求方法和控制器,例如:
GET|HEAD api/product-service/payment-notification
如果列表中没有对应记录,先清除路由缓存,再重新检查:
php artisan route:clear
php artisan cache:clear
php artisan route:list
生产环境如果需要缓存路由,可以在确认配置无误后运行:
php artisan route:cache
通过 route:list 可以直接判断问题出在路由注册阶段,还是控制器执行阶段。
改正控制器中的请求记录方式
下面这段代码还有一个问题:
file_put_contents(
storage_path().'/notification_'.$date.'.json',
\Response::json($request)
);
\Response::json() 返回的是 HTTP 响应对象,不适合直接作为 JSON 字符串写入文件。记录请求内容时,应先读取请求数据,再进行编码:
<?php
use Carbon\Carbon;
use Illuminate\Http\Request;
public function notification(Request $request)
{
$date = Carbon::now()->format('YmdHis');
file_put_contents(
storage_path('notification_'.$date.'.json'),
json_encode(
$request->all(),
JSON_UNESCAPED_UNICODE | JSON_PRETTY_PRINT
)
);
return response()->json([
'CREATED' => true,
], 201);
}
如果需要保存未经解析的原始请求体,可以使用:
file_put_contents(
storage_path('notification_'.$date.'.json'),
$request->getContent()
);
调试时,可以先把请求写入 Laravel 日志:
\Log::info('Payment notification received', [
'method' => $request->method(),
'headers' => $request->headers->all(),
'payload' => $request->all(),
'raw' => $request->getContent(),
]);
日志通常位于:
storage/logs/laravel.log
确认回调使用的 HTTP 方法
当前路由只接受 GET 请求:
Route::get(...)
支付通知、Webhook 和服务器回调通常使用 POST。如果对方发送的是 POST 请求,这条 GET 路由就不会执行,需要改成:
Route::post('product-service/payment-notification', [
'as' => 'productService.notification',
'uses' => 'ProductServiceController@notification',
]);
排查时也可以临时允许多种请求方法:
Route::match(
['get', 'post'],
'product-service/payment-notification',
'ProductServiceController@notification'
);
确认第三方平台使用的请求方法后,最好再将路由限制为对应的方法。
仍然无法访问时的检查顺序
- 运行
php artisan route:list,确认完整 URI 和请求方法。 - 如果路由位于
routes/api.php,使用带/api前缀的地址。 - 确认客户端发送的是
GET还是POST。 - 清除可能已经过期的路由缓存。
- 检查站点根目录是否指向 Laravel 项目的
public目录。 - 检查 Apache 或 Nginx 是否将请求正确转发到
public/index.php。 - 确认 Web 服务进程对
storage和bootstrap/cache目录有写入权限。 - 查看 Web 服务器的访问日志和错误日志。如果请求没有进入 Laravel,
storage/logs/laravel.log通常不会留下记录。
浏览器地址栏发出的通常是 GET 请求。因此,通过浏览器访问只能验证 GET 路由是否可达,不能证明第三方发送的 POST 回调能够正常处理。
最可能的直接原因是 routes/api.php 自动添加了 /api 前缀。之后还需要确认支付通知实际使用的 HTTP 方法。先运行 php artisan route:list 查看最终注册的路由,通常就能定位问题。
备注:内容仅供参考。
