类验证器使用 createQueryBuilder 意外触发验证

sfa*_*oso 5 validation restify typescript typeorm class-validator

我将Typeorm与class-validator结合使用,所以我定义了一个像这样的实体:

import {
    Entity,
    PrimaryGeneratedColumn,
    Column,
    BaseEntity,
    BeforeInsert,
    BeforeUpdate,
    getRepository
} from "typeorm";
import {
    validateOrReject,
    IsDefined,
} from "class-validator";
import errors from 'restify-errors';

@Entity()
export class License extends BaseEntity {
    @PrimaryGeneratedColumn('uuid')
    public id!: string;

    @Column({ nullable: false })
    @IsDefined({ message: 'name field was not provided' })
    public name!: string;

    @Column({ nullable: false })
    @IsDefined({ message: 'description field was not provided' })
    public description!: string;

    @BeforeInsert()
    @BeforeUpdate()
    async validate() {
        await validateOrReject(this, { skipUndefinedProperties: true });

        // Check if license already exists
        if ((await getRepository(License).count({ name: this.name }) > 0))
            throw new errors.BadRequestError(`There is already a license with name of ${this.name}`);
    }
}
Run Code Online (Sandbox Code Playgroud)

我还定义了一个运行 agetMany以从存储库检索数据的中间件Entity<T>,例如:

export default (entity: any) => async (req: Request, res: Response, next: Next) => {
    const repository = getRepository(entity);
    const query = repository.createQueryBuilder(`${entity}`);

    // Get query params
    for (const propName in req.params) {
        if (req.params.hasOwnProperty(propName)) {
            query.where(`${propName} = :param`, { param: req.params[propName] });
        }
    }

    // Pagination
    const page = parseInt(req.query.page, 10) || 1;
    const limit = parseInt(req.query.limit, 10) || 25;
    const startIndex = (page - 1) * limit;
    const endIndex = page * limit;

    const [result, total] = await query
        .skip(startIndex)
        .take(endIndex)
        .getManyAndCount();

    res.send({
        "success": true,
        "data": result,
        "count": total,
        "message": null
    });

    next();
};
Run Code Online (Sandbox Code Playgroud)

当我运行中间件时,该函数async validate()也会被触发,但它不应该被触发,因为我正在获取数据而不是插入或更新它们。事实上,当该validate方法被触发时,Typeorm会生成以下错误:

“QueryFailedError: ER_PARSE_ERROR: 您的 SQL 语法有错误;请检查与您的 MySQL 服务器版本相对应的手册,了解在“已经是名称为 ${this.name} 的许可证。A”附近使用的正确);\r\n });\r\n }\r\n}语法id。 12英寸

如果我注释该函数validate,数据将从中间件正确返回。

到底是怎么回事?这是图书馆的错误吗?

Aeg*_*ops 0

您的中间件似乎在查询中使用以下消息:There is already a license with name of ${this.name}。如果没有进一步说明如何将中间件与restify(server.use语句)一起使用,很难说这个错误消息有效负载如何以及为什么在中间件查询中结束,但您应该朝这个方向检查。

另一方面,如果您需要确保许可证名称的唯一性,您可能需要使用name以下符号声明唯一密钥,而不是使用手动验证:

@Column({ nullable: false, unique: true })
public name!: string;
Run Code Online (Sandbox Code Playgroud)

请在 typeorm 文档中找到更多信息:https://github.com/typeorm/typeorm/blob/master/docs/decorator-reference.md#column