I want to validate a parameter using a custom function. From reading the docs, it appears that this should work, but it's throwing an error.
config: {
validate: {
params: {
type: function(val, options, next){
next(null,val);
}
}
}
}
It crashes the server with this error:
[grunt-develop] > /Users/nathanloyer/Projects/ih-lumberjack/node_modules/hapi/node_modules/hoek/lib/index.js:678
throw new Error(msgs.join(' ') || 'Unknown error');
^
Error: Invalid schema content:
at Object.exports.assert (/Users/nathanloyer/Projects/ih-lumberjack/node_modules/hapi/node_modules/hoek/lib/index.js:678:11)
at Object.exports.schema (/Users/nathanloyer/Projects/ih-lumberjack/node_modules/hapi/node_modules/joi/lib/cast.js:65:10)
at internals.Object.keys (/Users/nathanloyer/Projects/ih-lumberjack/node_modules/hapi/node_modules/joi/lib/object.js:276:25)
at Object.exports.schema (/Users/nathanloyer/Projects/ih-lumberjack/node_modules/hapi/node_modules/joi/lib/cast.js:46:33)
at root.compile (/Users/nathanloyer/Projects/ih-lumberjack/node_modules/hapi/node_modules/joi/lib/index.js:113:21)
at Object.internals.compileRule (/Users/nathanloyer/Projects/ih-lumberjack/node_modules/hapi/lib/route.js:254:88)
at /Users/nathanloyer/Projects/ih-lumberjack/node_modules/hapi/lib/route.js:98:38
at Array.forEach (native)
at new module.exports.internals.Route (/Users/nathanloyer/Projects/ih-lumberjack/node_modules/hapi/lib/route.js:96:47)
at internals.Connection._addRoute (/Users/nathanloyer/Projects/ih-lumberjack/node_modules/hapi/lib/connection.js:352:17)
>> application exited with code 1
What am I doing wrong?
Try changing it to
params: function(val, options, next) {
next(null, val);
}
Thanks! That fixed the error, but what am I supposed to do with this practically? I mean, the val parameter contains nothing (empty object), the options contains a context, which contains headers, query, payload (which has my passed parameters), and auth.
How can I create a custom handler for a specific param the way Joi does it's handling? It's just a function too right? I want to test for specific values that are acceptable in one of my parameters, i.e.,
if (type == to 'sms' || type == 'email') -> all good
Also I'd just like to know how to harness the validation engine to have custom functions to test against.
Thanks!!
For instance, I now have this code which works properly:
params: function(val, options, next){
var type = options.context.payload.type;
if (type == "sms" || type == "email"){
next(null, val);
} else {
var err = new Error('Invalid type');
next(err, val);
}
}
But I'd only really like to run this on the "type" parameter. I also want to check if the "message" parameter is populated as it is required, and certain "type"s require certain other fields to be present as well. Reading up on Joi I see it provides this functionality to some extent, but I am unsure of how to integrate this into a custom function. If I can use Joi on each field and have some way to use a custom function that makes use of their .with() and .without() functionality, and also be able to use a custom function as described above, my validation problems would be solved. :-)
Is there a way to accomplish this?
No. You can not just slap functions into a Joi validation schema like that (yet). That being said, the logic you are using for type can easily be expressed with Joi so I don't think you need a custom function.
type: Joi.string().allow('sms', 'email')
Also
config: {
validate: {
params: function(val, options, next) {
}
}
}
val should be the parsed path parameters assuming your route has path parameters. If it's not, then there might be a bug.
Strong vote for a way to make a custom handler, even it is within the confines of Joi.
Ok, so .allow() works, what about .with()?
val is empty for me, the parameters are passed in the Request body as JSON.
console.log(val, options);
gives me:
{} { context:
{ headers:
{ 'content-type': 'application/json',
host: 'invisionheart.ngrok.io',
connection: 'close',
'user-agent': 'Paw/2.2.1 (Macintosh; OS X/10.10.3) GCDHTTPRequest',
'content-length': '103',
'x-forwarded-for': '162.231.221.16' },
query: {},
payload:
{ type: 'emails',
message: 'You have been NOTIFIED!!',
email: '[email protected]',
subject: 'Get some' },
auth: { isAuthenticated: false, credentials: null } } }
I'd like to see the payload in val, is that how it's intended to work?
I think there may be a bug here. When I do this (with the same payload above) I get an error:
params: {
type: Joi.string().allow('sms', 'email'),
message: Joi.string().required()
}
{"statusCode":400,"error":"Bad Request","message":"child \"message\" fails because [\"message\" is required]","validation":{"source":"params","keys":["message"]}}
Also, if I do this:
params: {
message: Joi.string().allow('sms', 'email')
}
It fails if I change the type in the payload to emailed, but passes if I put email. But the above validation is on the message parameter, not type?? What gives?
You're validating params, this has nothing to do with the payload.
@Marsup - params can be in the payload. A standard GET puts the parameters on the query string. A standard POST puts the parameters in the "payload" (the Request body) with the content-type application/x-www-form-urlencoded. Modern web apps often use a POST with the payload encoded as application/json instead.
So how is it that the params have nothing to do with the payload? Maybe I am misunderstanding what you are saying.
Look at the docs, you can have headers, params, query, or payload. You're using the wrong one.
I see. The first few times I read that page, it wasn't clear. Okay, so now I've got this:
payload: {
type: Joi.string().allow('sms', 'email'),
message: Joi.string().required()
},
options: {
allowUnknown: true
}
If I pass either one of those keys with an empty string, I get a 400 error as I expect. If I put in something other than 'sms' or 'email' however, I get a message saying 'invalid message type', but it's a 200 response. How can I make it send the appropriate error code when type is incorrect?
Is this one of your message ? A joi error would never cause a 200, else it's a bug.
I found the problem, if I change .allow( to .valid( the proper 400 error is returned. As I get more accustomed to it, Joi is shaping up to be a great tool! Thanks for your involvement with this product @Marsup !!
Just in case anyone else can benefit from this thread, here's what I ended up with, and without a custom validator. Thanks guys!
payload: {
type: Joi.string().valid('sms', 'email'),
message: Joi.string().required(),
email: Joi.string().email().when('type', { is: 'email', then: Joi.required() }),
number: Joi.string().when('type', { is: 'sms', then: Joi.required() })
},
options: {
allowUnknown: true
}
Thanks for posting @namlet :+1:
hello, i have a "similar probleme",
i need to validate the type of : query: { eType } but in a when in the body like this :
URL: /elasticSearch?eType=scroll&scroll=1h
METHOD: POST
BODY: {}
export const elasticSearchSchema = {
query: {
eType: Joi.string()
.valid(elasticSearchType.SCROLL, elasticSearchType.SEARCH)
.required()
},
body: {
query: Joi.when('eType', {
is: 'search',
then: Joi.required()
}),
scroll_id: Joi.when('eType', {
is: 'scroll',
then: Joi.string().required()
})
}
};
and the validation does not do what i need
thanks for your help
Please open an issue in https://github.com/hapijs/discuss instead. In this repository, issues are used only for bugs and incorrect/mising information in API.md.
This thread has been automatically locked due to inactivity. Please open a new issue for related bugs or questions following the new issue template instructions.
Most helpful comment
Just in case anyone else can benefit from this thread, here's what I ended up with, and without a custom validator. Thanks guys!