如何通过Guzzle将Symfony Request传递至另一个控制器?
解决Symfony网关转发文件上传请求的问题
核心原因
文件上传请求采用multipart/form-data格式,Symfony解析该请求时会一次性读取php://input流,导致后续调用$request->getContent()无法获取到原始请求体;直接转发字符串格式的请求体也无法正确还原multipart结构,最终导致文档微服务接收不到文件。
正确解决方案:重新构建Guzzle Multipart请求
网关需要基于Symfony解析后的参数和文件,重新构建符合Guzzle要求的multipart请求体,而不是直接转发原始请求内容。
步骤与代码示例
- 收集POST参数和上传文件
- 构建Guzzle的multipart数组
- 发送请求时使用
multipart选项而非body
use GuzzleHttp\Client; use Symfony\Component\HttpFoundation\File\UploadedFile; use Symfony\Component\HttpFoundation\Response; // 1. 从当前请求中获取解析后的参数和文件 $formParams = $request->request->all(); $uploadedFiles = $request->files->all(); // 2. 构建multipart请求数据 $multipartData = []; // 添加普通POST参数,处理数组类型参数 foreach ($formParams as $key => $value) { if (is_array($value)) { foreach ($value as $item) { $multipartData[] = [ 'name' => "{$key}[]", 'contents' => (string)$item ]; } } else { $multipartData[] = [ 'name' => $key, 'contents' => (string)$value ]; } } // 添加上传的文件 foreach ($uploadedFiles as $key => $file) { if ($file instanceof UploadedFile && $file->isValid()) { $multipartData[] = [ 'name' => $key, 'contents' => fopen($file->getRealPath(), 'r'), 'filename' => $file->getClientOriginalName() ]; } } // 3. 用Guzzle发送请求到文档微服务 $client = new Client(); $response = $client->post('http://your-document-service/upload-endpoint', [ 'multipart' => $multipartData, // 传递必要的请求头(如认证Token) 'headers' => [ 'Authorization' => $request->headers->get('Authorization'), // 无需手动设置Content-Type,Guzzle会自动生成带boundary的正确头 ] ]); // 返回微服务的响应给前端 return new Response( $response->getBody()->getContents(), $response->getStatusCode(), $response->getHeaders() );
关键注意事项
- 不要手动设置
Content-Type: multipart/form-data,Guzzle会根据multipart数组自动生成包含正确boundary的请求头,手动设置会导致格式错误。 - 确保文档微服务的控制器仍使用
$request->files->get('file_key')获取文件,和直接Postman调用的逻辑保持一致。 - 对于数组类型的参数(如
urls),需要拆分为多个name=key[]的项,否则微服务无法正确解析数组参数。
内容的提问来源于stack exchange,提问作者Augustin D'ille
相关产品推荐
相关产品推荐

