Laravel HTTP客户端如何对200含特定错误的响应执行重试?
Laravel HTTP客户端:对含特定错误的200响应执行重试
问题描述
对接的API存在两种需要重试的场景:
- 返回非客户端错误(如5xx状态码)
- 返回200状态码,但响应JSON中包含
errors字段且存在特定错误消息(如error but retry)
使用Laravel HTTP客户端的retry()闭包实现时,发现仅当请求抛出异常时闭包才会执行,导致含错误的200响应无法触发重试。当前代码能对502错误重试、遇到401停止,但不会处理含错误的200响应。
用户尝试的代码
$response = Http::acceptJson() ->retry([100,200], when: function($exception, $request) { if ($exception instanceof \Illuminate\Http\Client\RequestException) { return !$exception->response->clientError(); } if ($exception instanceOf \Illuminate\Http\Client\Response) { $response = $exception; if ($response->ok() && fluent($response->json())->collect('errors')->pluck('message')->contains('error but retry')) { return true; } } return false; }) ->get('http://localhost:3001/test')
模拟API返回顺序
- 502 Bad Gateway
- 200状态码,响应内容:
{"errors": [{"message": "error but retry"}]} - 200状态码,响应内容:
{"data": [{"one": 1}]} - 401 Unauthorized
解决方案
Laravel HTTP客户端的retry()默认仅在请求抛出异常时触发判断逻辑。要实现对200错误响应的重试,需主动将符合条件的200响应转换为异常,让retry机制捕获。
最终实现代码
$response = Http::acceptJson() ->retry( times: 3, sleep: [100, 200], when: function ($exception, $request) { // 处理非客户端错误的异常(如5xx) if ($exception instanceof \Illuminate\Http\Client\RequestException) { return !$exception->response->clientError(); } // 处理主动抛出的、携带响应的自定义异常 if ($exception instanceof \RuntimeException && property_exists($exception, 'response')) { return true; } return false; } ) ->get('http://localhost:3001/test') // 检查200响应是否包含需要重试的错误,是则抛出异常触发重试 ->throwIf(function ($response) { if ($response->ok()) { $errorMessages = collect($response->json('errors', []))->pluck('message'); return $errorMessages->contains('error but retry'); } // 非200响应交给默认异常处理(如401不会触发重试) return false; }, function ($response) { $exception = new \RuntimeException('Retryable error in 200 response'); $exception->response = $response; return $exception; });
关键逻辑说明
throwIf()的作用:在请求成功返回后,检查200响应是否包含目标错误消息。如果符合条件,抛出携带响应对象的自定义RuntimeException,触发retry机制。retry()闭包扩展:新增对自定义异常的判断,只要是我们主动抛出的异常,就返回true执行重试。- 重试次数控制:设置
times:3可覆盖模拟场景中的前三次请求(502→错误200→正常200),最终获取有效响应。 - 客户端错误处理:401这类4xx客户端错误会被
RequestException捕获,!$exception->response->clientError()返回false,因此不会触发重试,符合预期。
内容的提问来源于stack exchange,提问作者wivku
相关产品推荐
相关产品推荐

