Swoft2 框架精华教程:Controller 组件解析,使用说明

目录

[toc]

@Controller 注解

php 复制
/**
 * 表示路由最基础的前缀,此处为 `/`,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+"}

完整示例:

php 复制
/**
 * 此规则表示有一个 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 会直接返回设置的默认值(如有有的话)。

使用

php 复制
// 通过三个方法均可获得用户传递的参数
$request->getParsedBody();
// 获取单个校验过的数据
$page = $request->parsedBody('page');
$size = $request->parsedBody('size');
// 获取 GET 请求中的所有经过校验的参数数据
$query = $request->getParsedQuery();
// 获取路由参数经过校验器的值
$request->getParsedPath();
php 复制
/**
 * 获取内容,并且校验 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注解(用来标注具体的类,或者方法,只有被标注的对象在请求时候才会触发此中间件)。具体解析请查看中间件的优先级介绍。

php 复制
/**
 * @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 params
        if ($validateType === ValidateType::GET) {
            $query = $this->doValidate($query, $type, $name, $params, $validator, $fields, $unfields);
            continue;
        }

        // 如果校验器类型指定为path,则只校验路由的path参数
        // Route path params
        if ($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 全局函数来动态进行校验。

php 复制
// 此函数通过校验 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);
}