最可靠的方式是用 PHPUnit 的 assertStatus() 断言真实响应码,因其走完整请求生命周期,能暴露中间件、认证、验证等环节对状态码的真实影响。

直接测 status() 就行,但光看数字没用——得结合请求场景、中间件行为和前端预期一起验证。
用 PHPUnit 的 assertStatus() 断言真实响应码
这是最可靠的方式,因为走完整请求生命周期,能暴露中间件、认证、验证等环节对状态码的真实影响。
- 别在控制器里手动写
response('msg', 422)后就以为“返回了422”,它可能被后续中间件覆盖(比如 Sanctum 的 401 重定向逻辑) -
assertStatus(422)必须配合真实 HTTP 请求调用,例如$this->postJson('/api/login', [...]),不能只测返回值对象 - FormRequest 验证失败默认触发 422,但如果你在
failedValidation()里 throw 了HttpResponseException,状态码就取决于你传给response()->json(..., $code)的第二个参数 - 测试 401 时务必带
Accept: application/json头,否则 Laravel 会尝试重定向到login路由并报Route [login] not found
用 VSCode 的 REST Client 扩展做即时状态码核验
适合开发中快速确认某条路由当前返回什么状态码,但注意它不执行 PHPUnit 测试逻辑,容易漏掉异常路径。
- 在
.http文件里写请求时,必须显式加Accept: application/json(API 场景),否则auth:sanctum中间件可能返回 302 或 500 - 看到 302 不代表错了——
return back()就是故意返回 302,但 API 路由不该用这个;如果 API 接口返回 302,八成是中间件配置或路由分组没隔离好 - 用
Thunder Client时注意它默认不发送 Cookie,若测试带 session 的接口(如非 token 认证的 web api),要手动开启 “Send cookies”
HTTP 客户端发起请求时,$response->status() 和实际返回码不是一回事
这是最容易混淆的点:你用 Http::post() 调第三方 API,拿到的 $response->status() 是对方返回的状态码;而你自己的 API 接口返回码,是 Laravel 框架层生成的响应状态码,两者完全独立。
- 不要在控制器里写
Http::fake()后再测自己的接口返回码——Http::fake()只影响外发请求,不影响本应用响应生成逻辑 - 想测自己接口被调用时返回什么码,必须从外部发起请求(PHPUnit 或 REST Client),而不是在内部调用
Http::get() -
$response->failed()是判断第三方响应是否为 4xx/5xx,跟你的 API 返回码无关;它不会改变你当前响应的状态码
自定义状态码时,response(..., 422) 和 abort(422) 行为完全不同
前者直接生成响应,后者走异常流程,日志、中间件、全局 render() 都会介入,结果不可控。
-
response('msg', 422)最干净,适合单点控制;但要注意如果前面已输出内容(比如 dd() 或 echo),会报 “headers already sent” -
abort(422)会触发App\Exceptions\Handler的render()方法,如果你重写了它,返回的可能是 200 + 自定义 JSON,而不是 422 - 在 FormRequest 里改状态码,必须用
throw new HttpResponseException(response()->json([...], 400)),不能 return,否则 Laravel 已进入响应准备阶段 - 限流中间件
throttle:api固定返回 429,没法用上述方式覆盖——它在底层硬编码了状态码,要改就得重写ThrottleRequests类
真正难的不是设一个状态码,而是让同一个业务语义在所有路径(正常流程、验证失败、认证失败、中间件拦截)下都返回一致的状态码,并且前端能无歧义地识别。这点上,靠零散的 response() 很难兜住,必须靠测试驱动 + 统一出口约束。


















