Laravel 10中PUT请求携带multipart/form-data时请求内容为空的问题咨询
问题根源
这其实不是Laravel的限制,而是PHP本身的行为导致的:PHP默认只会自动解析POST请求的multipart/form-data内容,填充到$_POST和$_FILES全局变量中。对于PUT或PATCH这类请求,PHP不会自动处理multipart/form-data,所以Laravel的Request对象无法获取到任何表单或文件数据,就会出现$request->all()为空的情况。
你用POST请求加_method=PUT的方法欺骗能正常工作,正是因为此时请求本质是POST,PHP会自动解析multipart内容,Laravel再通过_method字段将请求视为PUT处理。
解决方案
如果你坚持要使用原生PUT方法而不依赖方法欺骗,有两种可行的处理方式:
方案1:手动解析PUT请求的Multipart内容(推荐)
你可以编写一个自定义中间件,在Laravel处理请求之前手动解析PUT类型的multipart/form-data内容,将表单数据和文件填充到Laravel的Request对象中。
- 创建中间件
在app/Http/Middleware目录下新建ParsePutMultipartFormData.php:
<?php namespace App\Http\Middleware; use Closure; use Illuminate\Http\Request; use Symfony\Component\HttpFoundation\Request as SymfonyRequest; class ParsePutMultipartFormData { public function handle(Request $request, Closure $next) { // 仅处理PUT方法的multipart请求 if ($request->isMethod('PUT') && $request->isMultipartContent()) { $contentType = $request->header('Content-Type'); // 提取multipart边界符 if (preg_match('/boundary=(.*)$/', $contentType, $matches)) { $boundary = trim($matches[1]); $rawContent = $request->getContent(); // 用Symfony的Request类手动解析multipart内容 $symfonyRequest = SymfonyRequest::create( $request->getUri(), 'POST', [], [], [], $request->headers->all(), $rawContent ); $symfonyRequest->headers->set('Content-Type', "multipart/form-data; boundary={$boundary}"); // 将解析后的表单数据和文件合并到Laravel的Request中 $request->merge($symfonyRequest->request->all()); foreach ($symfonyRequest->files->all() as $name => $file) { $request->files->set($name, $file); } } } return $next($request); } }
- 注册中间件
打开app/Http/Kernel.php,将中间件添加到API路由组的中间件列表中:
protected $middlewareGroups = [ 'api' => [ \App\Http\Middleware\ParsePutMultipartFormData::class, // 加入这一行 'throttle:api', \Illuminate\Routing\Middleware\SubstituteBindings::class, ], ];
- 调整Swagger配置
现在你可以将控制器上的@OA\Post注解改成@OA\Put,让Swagger UI直接发送PUT请求:
/** * @OA\Put( * path="/events/{id}", * tags={"Events"}, * summary="Etkinlik güncelle", * description="Belirtilen ID'ye sahip etkinliği günceller.", * security={{"sanctum": {}}}, * @OA\Parameter(name="id", in="path", required=true, description="Etkinlik ID'si", @OA\Schema(type="integer")), * @OA\RequestBody(ref="#/components/requestBodies/EventUpdateRequest"), * @OA\Response(response=200, ref="#/components/responses/StandardSuccess"), * @OA\Response(response=422, ref="#/components/responses/ValidationError"), * @OA\Response(response=404, ref="#/components/responses/NotFound"), * @OA\Response(response=401, ref="#/components/responses/Unauthorized"), * @OA\Response(response=403, ref="#/components/responses/Forbidden"), * @OA\Response(response=500, ref="#/components/responses/ServerError") * ) */
方案2:调整Swagger配置自动添加方法欺骗字段(最稳妥)
如果你不想修改代码,也可以保持现有逻辑,但调整Swagger配置,让Swagger UI在用户选择PUT方法时,自动发送POST请求并添加_method=PUT字段,这样既符合你的Swagger文档展示,又能正常工作:
修改你的EventUpdateRequest的Swagger注解,给_method字段添加default: "PUT"并设置为必填:
#[OA\RequestBody( request: "EventUpdateRequest", required: true, content: [ new OA\MediaType( mediaType: "multipart/form-data", schema: new OA\Schema( type: "object", properties: [ new OA\Property( property: "_method", type: "string", default: "PUT", required: true, example: "PUT" ), new OA\Property(property: "title", type: "string", example: "Updated Event"), new OA\Property(property: "sort_order", type: "integer", example: 1), new OA\Property(property: "image", type: "string", format: "binary"), new OA\Property(property: "pdf", type: "string", format: "binary") // ... 其他字段 ] ) ) ] )]
然后保持控制器的@OA\Post注解不变,用户在Swagger UI测试时,只需要填写表单和文件,Swagger会自动带上_method=PUT,无需手动添加。
总结
- 方法欺骗是Laravel官方推荐的处理方式,兼容性最好,适合大多数场景;
- 手动解析中间件可以实现原生PUT请求的multipart处理,但需要注意服务器的PHP配置(比如
enable_post_data_reading等)是否支持; - 两种方案都能解决你的问题,你可以根据自己的需求选择。
内容来源于stack exchange

