routing-controllers: Routing-Controllers + class-validator bug?

From 11000100111000 in typestack/class-validator#128

When using the automatic validation from routing-controllers for some reason the email “patch@patch” is valid when we are using these validators and the “patch” validator group

@IsEmail({
        allow_display_name: false,
        allow_utf8_local_part: true,
        require_tld: true
    }, {
        groups: ["post", "patch", "login"]
    })
    @IsOptional({
        groups: ["patch"]
    })
    public email;

Doing it the manual way like so works however

let user = new UserValidation();
user.email = "patch@patch";
validate(user, {groups: ["patch"]}).then(console.log);

About this issue

  • Original URL
  • State: closed
  • Created 6 years ago
  • Reactions: 8
  • Comments: 39 (12 by maintainers)

Most upvoted comments

I think I might be seeing similar issue too. Tried running a version of the auto validation based on the example in the README:

@JsonController()
class UserController {
    @Post('/login')
    login(@Body({validate: true}) user: User) {
        console.log('this is a valid body');
        return 'success!!!';
    }
}

class User {
    @IsEmail()
    email: string;

    @MinLength(6)
    password: string;
}

createExpressServer({
    controllers: [UserController]
}).listen(3000);

No matter what I send in the POST request I get a success back. Not sure if I’ve missed something in the above code but for the life of me I can’t get it to validate and throw an error.

Validating manually through the class-validator does work though.

So I have done few test and this is what I found:

library t1 t2 t3 t4 t5 t6 t7 t8 t9 t10
RC version 0.7.6 0.7.6 0.7.7 0.7.7 0.7.7 0.7.7 0.7.7 0.7.7 0.7.7 0.7.7
CV version 0.7.3 0.8.0-4 0.7.3 0.8.x 0.9.1 0.9.1 0.8.1 not in package.json 0.8.5 0.9.0
CT version 0.1.9 0.1.9 0.1.9 0.1.9 not in package.json 0.1.9 0.1.9 not in package.json 0.1.9 0.1.9
Working ?
normal as RC requires CV ^0.7.0

normal as RC requires CV ^0.8.1

So it seems to be a mix of class-validation / routing-controller changes breaking the validation, as the newer version of class-transformer works with routing-controller 0.7.6 and class-validator 0.7.3

@pleerock @NoNameProvided would you have any chance looking at this issue as it makes the stack (routing controller, class validator, class transfomers) non useable with latest version?

Thanks again 👍

import {ActionParameterHandler} from "routing-controllers/ActionParameterHandler";
import {validateOrReject as validate, ValidationError} from "class-validator";
import {BadRequestError} from "routing-controllers";

/**
 * issue https://github.com/typestack/routing-controllers/issues/384
 * a copy of https://github.com/typestack/routing-controllers/blob/4a56d176db77bc081dfcd3d8550e8433b5bc476e/src/ActionParameterHandler.ts#L179-L199
 * @param value
 * @param paramMetadata
 */
ActionParameterHandler.prototype["validateValue"] = function (this: any, value: any, paramMetadata: any) {
    const isValidationEnabled = (paramMetadata.validate instanceof Object || paramMetadata.validate === true)
        || (this.driver.enableValidation === true && paramMetadata.validate !== false);
    const shouldValidate = paramMetadata.targetType
        && (paramMetadata.targetType !== Object)
        && (value instanceof paramMetadata.targetType);

    if (isValidationEnabled && shouldValidate) {
        const options = paramMetadata.validate instanceof Object ? paramMetadata.validate : this.driver.validationOptions;
        return validate(value, options)
            .then(() => value)
            .catch((validationErrors: ValidationError[]) => {
                const error: any = new BadRequestError(`Invalid ${paramMetadata.type}, check 'errors' property for more info.`);
                error.errors = validationErrors;
                error.paramName = paramMetadata.name;
                throw error;
            });
    }

    return value;
};

Temporarily solved this problem. I don’t know why it works.

I found that it is sufficient to declare the dependency to class-validator to the same version as in routing-controllers: "class-validator": "^0.8.1",. I had to remove my node_modules folder and the lock file and run npm install again. After that here was no additional class-validator package in the node_modules folder for routing-controllers. That way the app and routing-controllers were using the exactly same version from the root nod_modules folder. No error anymore.

I’ve published @gritcode/routing-controllers that includes this fix (ie: class-validator as a peer dependency).

To use it: npm i @gritcode/routing-controllers class-validator

I’ve sent a PR back to this abandoned lib, but doubt it’ll ever be merged

What i did to fix it was to look in node_modules\routing-controllers\package.json, and copy the versions for class-validator and class-transformer (if you use that) over to my own package.json. And then download the dependencies again

A temporary fix that’s working for me is to remove class-validator from package.json, and instead just import from routing-controllers module. E.g.

import { IsNotEmpty } from 'routing-controllers/node_modules/class-validator';

The issue is caused because the version of class-validator that you have installed does not match the version of class-validator that routing-controllers is using. (see issue #424 and typestack/class-validator#166)

For a temporary fix you can run the following commands:

npm uninstall routing-controllers class-validator
npm install --save routing-controllers@0.7.7 class-validator@0.8.1

Edit: This will not work if you are using class-transformer. If you are using class-transformer read below for a solution

A temporary fix that’s working for me is to remove class-validator from package.json, and instead just import from routing-controllers module. E.g.

import { IsNotEmpty } from 'routing-controllers/node_modules/class-validator';

Anyone have better solution than this ? so far this is the only working solution for me.

Dependencies are

    "body-parser": "^1.19.0",
    "class-transformer": "^0.2.3",
    "cors": "^2.8.5",
    "express": "^4.17.1",
    "express-winston": "^3.1.0",
    "express-ws": "^4.0.0",
    "install": "^0.12.2",
    "jwt-simple": "^0.5.5",
    "mongoose": "^5.4.8",
    "mongoose-autopopulate": "^0.9.1",
    "multer": "^1.4.1",
    "reflect-metadata": "^0.1.13",
    "routing-controllers": "^0.7.7",

"class-validator": "^0.9.1", uninstalled package

I think the reason for the bug is that class-validator maintains an internal registry of the contracts that you decorate. If you install class-validator as a package dependency, you’ll likely get the newer one where your contracts will be registered; but when routing-controllers runs it uses its internal class-validator (v0.8.5) that would have an empty registry and hence not validate anything.

A proposed solution to routing-controllers: add class-validator as an optional-peer-dependency and force consumers that want to validate to add it themselves.

A proposed solution to people who declare contracts in their api project: don’t add class-validator to your project. By default if you add routing-controllers you’ll also get class-validator automatically, so just use taht

A proposed solution to people who keep contracts in a separate package that gets installed to their api: add class-validator as a dev-dependency in your contracts package. add routing-controllers to your api

@nolazybits I don’t think you will have a fix soon, looking at latest commit date. I forked it and just updated package.json versions to latest and it seems to work. This is the modified version you can use from npm until official release: https://www.npmjs.com/package/@mardari/routing-controllers

@Andrew5569 Temporary fix works. Cheers!

I created an example: https://github.com/Andrew5569/rc-body-validation

This is working for me

The problem might not be in routing-controllers directly but with transformation of plain object to class instance using class-transformer.