Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

schema validation with array parameter throws odd error when a single element is passed #704

Closed
ericsnap opened this issue Apr 26, 2019 · 5 comments

Comments

@ericsnap
Copy link

ericsnap commented Apr 26, 2019

As mentioned in this SO question, I was wondering how to handle optional, multi-value query parameters.
I want to check that the foo parameter is alphanumeric.
The query ?foo=bar&foo=baz yields req.query.foo = ['bar', 'baz'].
If I want to use express-validator to ensure that all values passed are alphanumeric, I see some odd error messages.

Here is my very simple code:

const express = require("express");
const { checkSchema, validationResult } = require("express-validator/check");

const app = express();

app.get(
    "/a",
    checkSchema({
        foo: {
            in: "query",
            isAlphanumeric: true,
            optional: true,
        },
    }),
    handler
);

app.get(
    "/b",
    checkSchema({
        'foo.*': {
            in: "query",
            isAlphanumeric: true,
            optional: true,
        },
    }),
    handler
);

app.listen(8888, () => {
    console.log(`listenning`);
});

function handler(req, res) {
    const errors = validationResult(req);
    if (!errors.isEmpty()) {
        res.status(400).send({ errors: errors.array() });
    } else {
        res.send({
            foo: req.query.foo,
        });
    }
}

if I call it with:
curl 'localhost:8888/a?foo=barone&foo=bar%20two'
I get
{"foo":["barone","bar two"]}
because /a only validates 1 value of foo.

if I call it with:
curl 'localhost:8888/b?foo=barone&foo=bar%20two'
I get
{"errors":[{"location":"query","param":"foo[1]","value":"bar two","msg":"Invalid value"}]}
which is to be expected.

Unexpected behavior
However, things get wonky when I only pass a single value to foo.

if call
curl 'localhost:8888/a?foo=bar%20two'
I get
{"errors":[{"location":"query","param":"foo","value":"bar two","msg":"Invalid value"}]}
which is correct

but if I call
curl 'localhost:8888/b?foo=bar%20two'
I get
{"errors":[{"location":"query","param":"foo[3]","value":" ","msg":"Invalid value"}]}

That last message is odd. I would expect:

  • either a simple param: 'foo'
  • or somewhat detect the wildcard and say param: 'foo[0]' in the case of a single element

I put together a runkit notebook if you and to check it out live or hit the live endpoint.

@adarshmadrecha
Copy link

Try This,

var express = require("@runkit/runkit/express-endpoint/1.0.0");
const { query, validationResult } = require("express-validator/check");
var app = express(exports);

app.get("/a",[
    query('foo').isArray(),
    query('foo.*').isAlphanumeric().optional()
],handler
);

function handler(req, res) {
    const errors = validationResult(req);
    if (!errors.isEmpty()) {
        res.status(400).send({ errors: errors.array() });
    } else {
        res.send({
            foo: req.query.foo,
        });
    }
}

@gustavohenke
Copy link
Member

There's a bit of legacy stuff here... standard validators (e.g. isAlphanumeric) require string values, and arrays only get their first result validated 😕

Whereas if you use a wildcard in the selector, but pass a string value, it will validate each of the string characters.

I'll take a look if it's possible to fix this without causing a breaking change.
I think there was someone requesting it to work this way in the past.

In the mean time, suggestions are appreciated!

@ericsnap
Copy link
Author

@gustavohenke so, I think I have found a simple workaround/fix.
Thanks to your sanitizers, I can do:

app.get(
    "/b",
    checkSchema({
       'foo' : {
           toArray: true,
       },
        'foo.*': {
            in: "query",
            isAlphanumeric: true,
            optional: true,
        },
    }),
    handler
);

And it seems to do the trick. And it actually works pretty well as it guarantees me an array on the other end (before, I had to check for a single value and put it in an array).

@itzjonas
Copy link

itzjonas commented Apr 28, 2020

@ericsnap your solution almost works for me! Sadly isIn doesn't like what we're doing:

const querySchema = {
    placement: { toArray: true },
    'placement.*': {
        in: 'query',
        isIn: {
            options: ['checkbox', 'flyout', 'main'],
        },
        optional: true,
    },
};

router.post('/', [
    checkSchema(querySchema),
], (req, res) => { ... }

And when I make a call to /page?placement=checkbox&placement=flyout I get the following errors:

{
    "errors": [
        {
            "value": "flyout",
            "msg": "Invalid value",
            "param": "placement[1]",
            "location": "query"
        }
    ]
}

EDIT:
I forgot to put an array inside an array for options :( Leaving comment for others :D

isIn: {
    options: [['checkbox', 'flyout', 'main']],
},

@fedeci fedeci mentioned this issue Feb 23, 2021
9 tasks
@gustavohenke gustavohenke added this to the v7.0.0 milestone Apr 13, 2023
@gustavohenke
Copy link
Member

Hi hi, https://github.com/express-validator/express-validator/releases/tag/v7.0.0 is out with a fix for this 🙂
Please give it a try!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

4 participants