ThinkPHP 6 的 response() 默认只构建响应对象而不自动发送,需显式调用 send() 或在控制器中 return 才生效;中间件等非控制器场景必须手动 send(),否则响应体为空;json() 自动设置正确 Content-Type,raw() 等需手动指定;文件下载须避免前置输出,大文件应流式处理;自定义类型需提前注册 MIME。

ThinkPHP 6 的 response() 函数默认不自动输出内容
很多人调用 response()->json(['code'=>0]) 后页面空白,或返回值没出现在 HTTP 响应体里——这不是 bug,是设计:ThinkPHP 6 的响应对象默认只构建,不自动发送。你得手动触发输出。
常见错误现象:curl -I 看到状态码是 200,但响应体为空;浏览器打开接口返回空白页;Postman 显示“no response body”。
- 必须显式调用
send()才能真正输出:response()->json(['data'=>1])->send() - 在控制器方法中直接
return response()->json(...)是安全的,框架会自动调用send()(前提是没提前exit或die) - 如果在中间件、钩子或非控制器逻辑里用
response(),务必自己加->send(),否则静默失效 - 调用
send()后再写echo或输出其他内容,会导致“headers already sent”错误
Content-Type 不匹配导致前端解析失败
ThinkPHP 默认的 json() 方法设的是 application/json,但如果你手动拼接字符串再用 raw(),Content-Type 很可能还是 text/html,前端 fetch().json() 就会报 Unexpected token。
-
response()->json($data)✅ 自动设Content-Type: application/json; charset=utf-8 -
response($data)->contentType('application/json')❌ 缺少 charset,某些 IE 或旧版安卓 WebView 会乱码 -
response()->raw(json_encode($data))❌ 默认 Content-Type 是text/html,必须手动->contentType('application/json; charset=utf-8') - 中文字段用
json_encode($data, JSON_UNESCAPED_UNICODE)避免 \uXXXX,但注意这和 ThinkPHP 内置json()行为一致,无需额外处理
文件下载响应容易忽略 header 冲突和缓存
用 response()->download() 返回文件时,如果前面已有输出(哪怕一个空格、BOM、var_dump()),就会报 Cannot modify header information。
立即学习“PHP免费学习笔记(深入)”;
- 确保控制器方法里没有
echo、print_r()、var_dump(),连调试日志都别打在响应前 -
download()默认带Cache-Control: no-cache,但部分 Nginx 配置会覆盖它,建议显式加->header('Cache-Control', 'no-cache') - 文件名含中文要用
rawurlencode()处理:response()->download($path, rawurlencode('报告.pdf')),否则 Chrome 可能截断或乱码 - 大文件别用
download()直接读入内存,改用stream()+fopen('php://output')流式输出
自定义响应类型要小心 MIME 类型注册和输出顺序
想支持 .xlsx 或 application/vnd.openxmlformats-officedocument.spreadsheetml.sheet 这类类型?ThinkPHP 不内置识别,得自己注册,否则 response()->type('xlsx') 会 fallback 成 text/html。
- 在
app/common.php或服务提供者里加:think\Response::mimeType('xlsx', 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet') - 注册后才能用
response()->type('xlsx')->content($bytes),否则type()方法无效 - 调用
type()必须在content()或file()之前,顺序反了 MIME 不生效 - 如果用了 Swoole 或 RoadRunner,响应头注册需在 Worker 启动前完成,运行时注册可能只对当前请求生效



















