slim 4 基于 psr-7/psr-15,request/response 不可变,所有操作必须接收返回值;body 流只能读一次,应优先用 getparsedbody() 或强转 (string);响应须显式设 content-type 和 status;上传文件须用 getuploadedfiles()。

Slim 4 默认就是 PSR-7,但你得用对容器和中间件
Slim 4 完全基于 PSR-7 和 PSR-15,Request 和 Response 对象天然不可变,所有修改都必须返回新实例。很多人升级后发现 $request->getBody()->getContents() 为空、或 $response->withHeader() 没生效,本质是忘了“返回值必须被接收”。
常见错误现象:Uncaught TypeError: Argument 1 passed to Slim\App::__invoke() must be an instance of Psr\Http\Message\ServerRequestInterface —— 这说明你手动 new 了 Request,绕过了 Slim 内置的 PSR-7 工厂。
- 不要手动 new
ServerRequest或Response,全部通过$request/$response参数传入 - 所有链式调用(如
withHeader()、withStatus()、withAddedHeader())都必须赋值给变量,原对象不变 - 中间件签名必须是
function (Request $request, Response $response, callable $next): Response,且必须 return$next($request, $response)的结果
读取 JSON 请求体时 body 流只能读一次
PSR-7 的 StreamInterface 是单次读取的,$request->getBody()->getContents() 调用一次后流就 EOF 了,后续再读会返回空字符串。这在验证、日志、路由前处理等场景极易踩坑。
使用场景:API 接收 application/json,需要先解析 body 做权限校验,再交给路由处理。
- 用
$request->getParsedBody()—— Slim 自动缓存并解析 JSON/表单,安全可多次调用 - 若需原始 JSON 字符串(比如做签名验签),先用
$body = (string) $request->getBody()强转,它内部会 rewind 并读取全部内容 - 避免直接调用
getContents()或__toString()多次;更别在中间件里反复json_decode((string)$request->getBody())
自定义响应时别漏掉 Content-Type 和 Status
PSR-7 响应默认没有 Content-Type,也没有状态码(隐式为 200),但前端(尤其 fetch/Axios)依赖这些头做解析。Slim 不会自动补全,不设就等于没设。
常见错误现象:JSON 返回被浏览器当 text/html 渲染,或 Axios 报 Unexpected token —— 实际是服务端没发 Content-Type: application/json。
- 返回 JSON 时务必链式设置:
$response->withHeader('Content-Type', 'application/json')->withStatus(201) -
withStatus()必须显式调用,否则始终是 200;4xx/5xx 错误尤其不能省 - 如果用了
JsonResponse类(如 slim/slim-skeleton 中的封装),确认它内部是否已 setHeader + withStatus
上传文件要用 $request->getUploadedFiles(),不是 $_FILES
PSR-7 将上传文件抽象为 UploadedFileInterface 数组,结构与传统 $_FILES 不同。直接读 $_FILES 会绕过 PSR-7 生命周期,丢失错误码、临时路径封装等保障。
性能影响:PSR-7 的 moveTo() 方法底层仍是 move_uploaded_file(),无额外开销;但封装后能统一处理移动失败、权限异常等。
- 用
$files = $request->getUploadedFiles()获取关联数组,每个值是UploadedFile实例 - 检查上传状态:
$file->getError() === UPLOAD_ERR_OK,别用$file['error'] - 移动文件:
$file->moveTo('/path/to/save/' . $file->getClientFilename()),注意目标目录需有写权限
PSR-7 的不可变性不是约束,是明确数据流向的契约。最常被忽略的,是把 $response 当成可变对象去“修改”,却忘了每一步操作都在生成新实例——漏掉那个 return,整个响应就卡在中间件里了。
php免费学习视频:立即使用
踏上前端学习之旅,开启通往精通之路!从前端基础到项目实战,循序渐进,一步一个脚印,迈向巅峰!











