PHP Swoft2 框架精华系列:Validator 校验器详解

发布于:2025-06-19 ⋅ 阅读:(15) ⋅ 点赞:(0)

校验器

校验器是 swoft2 中一个常用的组件。

校验器在 RPC服务、WS服务、HTTP 服务中均有涉及,用来校验客户端上传到服务端的数据是否合法。

校验器类型

校验器一般分为两种,系统校验器,和自定义校验器。

系统校验器是通过 @Validator 标签进行注解的一个校验器类(没有方法只有属性),每个属性上通过不同的注解(如:@Length @IsString )来说明校验时候参数要符合的规则。

自定义校验器,同样需要 @Validator 标签对校验的类进行注解,但是类必须要实现 ValidatorInterface 接口,只有实现接口validate(array $data, array $params),才会称为自定义校验器(源码中有此判断,参见:src/Annotation/Parser/ValidatorParser.php)。注意自定义校验器,只会校验 body 中数据,且自定义校验器,只会使用 body 中数据和 params,其他 @Validate 属性,并不会使用。

/**
 * Class CustomerValidator
 *
 * @since 2.0
 *
 * @Validator(name="userValidator")
 */
class CustomerValidator implements ValidatorInterface
{
   
    /**
     * @param array $data		这是自定义校验器中
     * @param array $params
     *
     * @return array
     * @throws ValidatorException
     */
    public function validate(array $data, array $params): array
    {
   
        $start = $data['start'] ?? null;
        $end   = $data['end'] ?? null;
        if ($start === null && $end === null) {
   
            throw new ValidatorException('Start time and end time cannot be empty');
        }

        if ($start > $end) {
   
            throw new ValidatorException('Start cannot be greater than the end time');
        }

        return $data;
    }
}

@Validate 注解

属性说明
/**
 * Class
 *
 * @since 2.0
 *
 * @Annotation
 * @Target("METHOD")
 * @Attributes({
 *     @Attribute("validator", type="string"),
 *     @Attribute("fields", type="array"),
 *     @Attribute("params", type="array"),
 *     @Attribute("message", type="string"),
 * })
 */
class Validate {
   }

以上为 @Validate 注解的参数要求,具体说明见下表:

注解参数 参数类型 是否必须 备注
validator 字符串 已经定义好的校验器的名字
fields 数组 校验器中的属性(对应请求中的参数)。
不指定,默认校验校验器中所有属性。
如果指定的值在校验器中不存在,那么控制器中仍然可以接收到参数,但是不会有任何校验!!!
unfields 数组 不进行校验的属性
params 数组 用在自定义校验器中,用户手动传递到校验器的数据
注:自定义校验器时候才会使用
message 字符串 校验失败时候提示信息
type 字符串 校验数据所在请求对象中的位置(get/body/path)

@Validate 注解用于 method 上,示例:

// 示例1:通过系统默认校验器,校验 post 请求中的参数
/**
 * @RequestMapping("account")
 * @param Request $request
 * @param Response $response
 * @return Response
 * @throws InvalidArgumentException
 * @Validate(validator="userValidator", fields={"name", "password"}, type="body")
 */
public function account(Request $request, Response $response): Response;

// 示例2:通过系统默认的校验器校验自定义 path 参数

/**
 * @RequestMapping(route="/[list-{page}.html|index.html]", params={"page"="[2-9]\d*|1\d+"}, method={"GET"})
 * @View("home/index")
 * @Validate(validator=PageListDto::class, fields={"page", "size"}, type="path")
 *
 * @param Request $request
 * @param Response $response
 * @return Response
 */
public function index(Request $request, Response $response): Response

注意:@Validate 的 type 参数十分重要,默认值为 body,也就是默认校验 post 请求参数。如果是校验 get 参数,或者自定义 path 中的自定义参数,必须要写明类型。否则校验出错。

校验器校验主要流程

以 HTTP 服务为例,可以参照 http-server 组件中的 src/Middleware/ValidatorMiddleware.php 中间件,此中间件可以通过定义核心 Bean 的方式,将其挂载到 HTTP 服务上。从相关代码可以看出,中间件运行期间,通过 ValidateRegister::getValidates()方法提取访问接口上绑定的校验器相关信息。

class ValidatorMiddleware implements MiddlewareInterface
{
   
    /**
     * @param ServerRequestInterface  $request
     * @param RequestHandlerInterface $handler
     *
     * @return ResponseInterface
     * @throws ValidatorException
     */
    public function process(ServerRequestInterface $request, RequestHandlerInterface $handler): ResponseInterface
    {
   
        // 获取路由匹配结果,如果未匹配成功,此中间件不做处理,交给下一个中间件。
        /* @var Route $route */
        [$status, , $route] = $request->getAttribute(Request::ROUTER_ATTRIBUTE);
        if ($status !== Router::FOUND) {
   
            return $handler->handle($request);
        }
		// 如果路由匹配成功,获取路由绑定的处理器(controller/action)
        // Controller and method
        $handlerId = $route->getHandler();
        [$className, $method] = explode('@', $handlerId);

        // 获取 controller/action 方法上通过注解绑定的校验器(可以多个)
        // Query validates
        $validates = ValidateRegister::getValidates($className, $method);
        // 如果没有,说明不用校验,交给下一个中间件处理
        if (empty($validates)) {
   
            return $handler->handle($request);
        }
		// 获取校验涉及的相关数据
        $data  = $request->getParsedBody();
        $query = $request->getQueryParams();
        $path  = $route->getParams();

        // ParsedBody is empty string
        $parsedBody    = $data = empty($data) ? [] : $data;
        $notParsedBody = !is_array($data);
        if ($notParsedBody) {
   
            $parsedBody = [];
        }

        // 获取校验器组件的实例对象
        /* @var Validator $validator */
        

网站公告

今日签到

点亮在社区的每一天
去签到