Hapi.js Validation with Joi + failAction
question.
We want to build a "traditional" server-side-only rendered application using Hapi.
I'm trying to understand how to avoid returning a "raw" 400
error to the client when Joi
validation fails:
We want to intercept this "email not allowed to be empty" validation error and display it in the html template back to the client,
instead of simply returning the 400
error.
@AdriVanHoudt advised that we should:
"Look at failAction under http://hapijs.com/api#route-options "
So we added failAction: 'log'
to the /register
route handler:
{
method: '*',
path: '/register',
config: {
validate: {
payload : register_fields,
failAction: 'log'
}
},
handler: register_handler
}
See code in: server.js
the register_handler
is:
function register_handler(request, reply, source, error) {
console.log(request.payload);
console.log(' - - - - - - - - - - - - - - - - - - - - -');
console.log(source)
console.log(' - - - - - - - - - - - - - - - - - - - - -');
console.log(error)
return reply('welcome!');
}
I am expecting to see an error in the terminal/console
but when I try to console.log
the handler
:
- - - - - - - - - - - - - - - - - - - - -
undefined
- - - - - - - - - - - - - - - - - - - - -
undefined
I asked the question on GitHub: https://github.com/hapijs/joi/issues/725 but have not yet got an answer with a good example. Full code if you have time to help: https://github.com/nelsonic/hapi-validation-question
Express-validator So by definition, we can say that: Joi can be used for creating schemas (just like we use mongoose for creating NoSQL schemas) and you can use it with plain Javascript objects. It's like a plug n play library and is easy to use. On the other hand, express-validator uses validator.
Joi is an object schema description language and validator for JavaScript objects. Joi allows you to create blueprints or schemas for JavaScript objects to ensure validation of key information.
@hapi/joi is deprecated #3430 This isn't exactly a bug, but joi is moving out of the @hapi org. Probably a good idea of docusaurus v2 moves to the current version, too.
server.ext('onPreResponse' ...
As noted by @Clarkie a generic way of catching all errors in your Hapi App is to use 'onPreResponse'
.
We wrote a Hapi Plugin that does exactly that: https://www.npmjs.com/package/hapi-error
As usual it has:
And lets you define your own custom error pages in 3 easy steps.
npm install hapi-error --save
Include the plugin when you register
your server:
See: /example/server_example.js for simple example
error_template
Note:
hapi-error
plugin expects you are usingVision
(the standard view rendering library for Hapi apps) which allows you to use Handlebars, Jade, React, etc. for your templates.
Your error_template.html
(or error_template.ext
error_template.jsx
) should make use of the 3 variables it will be passed:
errorTitle
- the error tile generated by Hapi
statusCode
- *HTTP statusCode sent to the client e.g: 404
(not found)errorMessage
- the human-friendly error message
for an example see:
/example/error_template.html
That's it!
failAction
We added failAction
which re-uses the register_handler
so that the registration-form.html
is shown with any input validation error message (until it is submitted with valid data)
{
method: '*',
path: '/register',
config: {
validate: {
payload : register_fields,
failAction: register_handler // register_handler is dual-purpose (see below!)
}
},
handler: register_handler
}
the register_handler
is:
function register_handler(request, reply, source, error) {
// show the registration form until its submitted correctly
if(!request.payload || request.payload && error) {
var errors, values; // return empty if not set.
if(error && error.data) { // means the handler is dual-purpose
errors = extract_validation_error(error); // the error field + message
values = return_form_input_values(error); // avoid wiping form data
}
return reply.view('registration-form', {
title : 'Please Register ' + request.server.version,
error : errors, // error object used in html template
values : values // (escaped) values displayed in form inputs
}).code(error ? 400 : 200); // HTTP status code depending on error
}
else { // once successful, show welcome message!
return reply.view('welcome-message', {
name : validator.escape(request.payload.name),
email : validator.escape(request.payload.email)
})
}
}
See: server.js:57 for complete file.
Where extract_validation_error(error)
and return_form_input_values(error)
are helper functions defined within server.js
(but would be split out into re-useable view helpers) which keep our handler function lean.
When we submit the form without any of the required fields we see:
We also use https://github.com/chriso/validator.js to mitigate Cross Site Scripting vulnerability:
And display a welcome message on successful registration:
We feel that re-using the handler function as the failAction
keeps the code related to this route/action in a single place
whereas server.ext('onPreResponse' ...
(while appropriate on initial inspection) will introduce "hooks"
which can be a source of confusion (once an app has many such hooks...)
Let us know what you think!
You should look at implementing an error handler in the onPreResponse
extension point.
The response contained in request.response may be modified (but not assigned a new value). To return a different response type (for example, replace an error with an HTML response), return a new response via reply(response). Note that any errors generated after reply(response) is called will not be passed back to the
onPreResponse
extension method to prevent an infinite loop.
A simple example:
server.ext('onPreResponse', function (request, reply) {
if (request.response.statusCode === 400 ){
return reply('summat else');
}
return reply.continue();
});
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With