PHP Swoft2 框架精华系列:Controller 控制器组件解析,用法详解
@Controller 注解
/*** 表示路由最基础的前缀,此处为 `/`,RequestMapping 中引入的 @prefix 就是此处的 prefix 值* @Controller("/")* @Controller(prefix="/")* * 可以不用写 `/`, 会自动加上,表示 `/user` 前缀的路由* @Controller("user")*/
路由设置
@RequestMapping
注解,用来获取用户自定义的 http 路由规则。
-
route,字符串类型
配置路由的路径,可以加入中括号,括起来的部分表示可选路由(可有可无),自定义参数用花括号中间写名字的形式来书写(例:
@prefix_index[-{page}]
), @prefix 表示 @Controller 注解中的 prefix 值。 -
name,字符串
路由名称
-
method,数组类型
决定被请求控制器的操作允许哪种请求方式 POST/GET,或者两者都配置上
-
params
配置参数的匹配规则,以此来确定路由是否能够匹配。以上方 page 为例:
params={"page"="\d+"}
完整示例:
/*** 此规则表示有一个 en_name 的参数,且此参数必须匹配正则 [A-Za-z0-9\-\_]+ 此调路由才能匹配* @RequestMapping(route="{en_name}/", params={"en_name"="[A-Za-z0-9\-\_]+"}, method={"GET"})* 此规则表示,为 / 或者 /index.html 或者 /list-12.html 这种形式* @RequestMapping(route="/[list-{page}.html|index.html]", params={"page"="[2-9]\d*|1\d+"}, method={"GET"})*/
参数传递
-
getParsedBody
如果请求 body 中有参数,且方法校验规则 @Validate 标签 type 不设置(默认为 body),或者设置为 body,则校验规则会校验 body 中的参数,通过 getParsedBody 方法获取到的为校验规则校验且修改(比如默认值)之后的参数的值。
-
getParsedQuery
如果请求 query 中有参数,且方法校验规则 @Validate 注解 type 设置为 get,则校验规则会校验 query 中的参数。通过 getParsedQuery 方法获取到的,为校验器校验且修改之后的参数值。
-
getParsedPath
如果路由参数(路径中参数)有传值,且方法校验规则 @Validate 注解设置 type 为 path,则校验规则会校验path 中的参数。通过此方法获取到的,为校验器校验且修改之后的参数值。
注意:如果没有添加校验器,则获取值为null(详见 ValidatorMiddleware::class)。必须有合法校验器,才有可能通过以上方法获取到值。
getParsedBody=getBody
getParsedQuery=getQueryParams
getParsedPath=getQueryParams
GET 请求,不能通过body 获取参数,如果使用body,校验器校验不到相应的参数,getParsedBody 会直接返回设置的默认值(如有有的话)。
使用
// 通过三个方法均可获得用户传递的参数
$request->getParsedBody();
// 获取单个校验过的数据
$page = $request->parsedBody('page');
$size = $request->parsedBody('size');
// 获取 GET 请求中的所有经过校验的参数数据
$query = $request->getParsedQuery();
// 获取路由参数经过校验器的值
$request->getParsedPath();
/*** 获取内容,并且校验 query 中的参数值* @RequestMapping("list", method="GET")* @Validate(validator=PageListDto::class, fields={"page", "size"}, type="query")** @param Request $request* @param Response $response* @return Response* @throws ApiException*/
校验器源码解析
所有的校验触发,均是通过 http-server 组件中的 httpDispatcher
分发器,中设置的 middlewares 来触发的,负责校验的中间件为 ValidatorMiddleware::class
。
注意分发器中设置的 middlewares 为全局分发器(即所有请求都会触发此处配置的中间件),如果有选择的配置中间件,可以使用
@Middlewares
或者@Middleware
注解(用来标注具体的类,或者方法,只有被标注的对象在请求时候才会触发此中间件)。具体解析请查看中间件的优先级介绍。
/*** @param array $body* @param array $validates* @param array $query* @param array $path** @return array* @throws ValidatorException*/
public function validateRequest(array $body, array $validates, array $query = [], array $path = []): array
{// 循环遍历当前控制器方法上注解的校验规则foreach ($validates as $name => $validate) {$validator = ValidatorRegister::getValidator($name);if (empty($validator)) {throw new ValidatorException(sprintf('Validator(%s) is not exist!', $name));}// 获取当前校验器类型,是系统校验器还是自定义校验器$type = $validator['type'];$fields = $validate['fields'] ?? [];$unfields = $validate['unfields'] ?? [];$params = $validate['params'] ?? [];// 获取当前校验规则的类型(校验get、body、还是path)$validateType = $validate['type'];// 如果校验器类型指定为get,则只校验匹配的get类型参数// Get query paramsif ($validateType === ValidateType::GET) {$query = $this->doValidate($query, $type, $name, $params, $validator, $fields, $unfields);continue;}// 如果校验器类型指定为path,则只校验路由的path参数// Route path paramsif ($validateType === ValidateType::PATH) {$path = $this->doValidate($path, $type, $name, $params, $validator, $fields, $unfields);continue;}// 如果未指定,默认校验body参数$body = $this->doValidate($body, $type, $name, $params, $validator, $fields, $unfields);}return [$body, $query, $path];
}
另外,对于比较复杂的校验,比如数组内部元素的一些具体校验规则,默认是没有提供的,可以通过自定义的校验规则来完成类似的校验。或者直接通过 validate
全局函数来动态进行校验。
// 此函数通过校验 validator 单例对象来触发校验
function validate(array $data,string $validatorName,array $fields = [],array $userValidators = [],array $unfields = []
): array {/* @var Validator $validator */$validator = BeanFactory::getBean('validator');return $validator->validate($data, $validatorName, $fields, $userValidators, $unfields);
}