Yii2 REST框架文件上传及multipart请求下auth_key校验问题
Yii2 REST控制器multipart请求auth_key校验适配方案
前置配置校验
首先确保你实现的multipart解析器已经注册到请求组件的解析器配置中,Yii会在请求初始化阶段自动调用解析器处理multipart内容,解析后的普通表单参数会直接注入bodyParams:
// config/web.php 对应组件配置 return [ 'components' => [ 'request' => [ 'enableCsrfValidation' => false, // REST接口需关闭CSRF校验 'parsers' => [ 'multipart/form-data' => 'your\custom\namespace\MultipartParser', // 替换为你自己的解析器类 ], ], // 其他组件配置 ], ];
checkAccess方法适配逻辑
统一使用getBodyParam()方法获取auth_key,该方法会自动适配普通x-www-form-urlencoded类型POST和已解析的multipart类型POST的参数,同时加rawBody兜底兼容逻辑,避免解析器未提前触发的场景:
public function checkAccess($action, $model = null, $params = []) { // 优先从已解析的参数中取auth_key,适配所有已注册解析器的请求类型 $authKey = \Yii::$app->request->getBodyParam('auth_key'); // 兜底逻辑:multipart请求下解析参数为空时从rawBody提取 if ( empty($authKey) && str_contains(\Yii::$app->request->getContentType(), 'multipart/form-data') ) { $rawBody = \Yii::$app->request->getRawBody(); // 正则匹配multipart报文中的auth_key字段值,可根据实际报文格式调整正则 if (preg_match('/name="auth_key"\s*\r\n\r\n(.*?)\r\n/', $rawBody, $matches)) { $authKey = trim($matches[1]); } } // 原有auth_key校验逻辑 if (empty($authKey) || !$this->validateAuthKey($authKey)) { throw new \yii\web\ForbiddenHttpException('身份验证失败'); } parent::checkAccess($action, $model, $params); }
解析器规范要求
你自定义的multipart解析器需要实现yii\web\RequestParserInterface接口,parse()方法返回的数组会自动被Yii赋值给请求的bodyParams属性,参考实现:
namespace your\custom\namespace; use yii\web\RequestParserInterface; class MultipartParser implements RequestParserInterface { public function parse($rawBody, $contentType) { $params = []; // 提取boundary if (!preg_match('/boundary="?([^"\s]+)"?/', $contentType, $boundaryMatch)) { return $params; } $boundary = $boundaryMatch[1]; $parts = explode("--{$boundary}", $rawBody); foreach ($parts as $part) { $part = trim($part); if (empty($part) || $part === '--') continue; // 拆分头部和内容块 $splitPosition = strpos($part, "\r\n\r\n"); if ($splitPosition === false) continue; $headerBlock = substr($part, 0, $splitPosition); $body = substr($part, $splitPosition + 4); // 解析Content-Disposition头 if (preg_match('/Content-Disposition: form-data; name="([^"]+)"/i', $headerBlock, $nameMatch)) { $fieldName = $nameMatch[1]; // 普通表单字段存入params数组,文件字段可自行处理存入$_FILES或自定义结构 if (!preg_match('/filename=/i', $headerBlock)) { $params[$fieldName] = rtrim($body, "\r\n"); } } } return $params; } }
注意事项
- 不要直接调用原生
$_POST/$_FILES全局变量,Yii REST模式下默认不会填充这两个变量,统一使用\Yii::$app->request->getBodyParam()获取普通参数,\yii\web\UploadedFile::getInstanceByName()获取上传文件 - 如果auth_key同时支持放在header头中,可在
checkAccess最前端增加从header头取参数的逻辑,优先级可根据业务需求调整
内容的提问来源于stack exchange,提问作者Néstor
相关产品推荐
相关产品推荐

