Reputation: 33134
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
Upvotes: 7
Views: 5106
Reputation: 33134
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 HapistatusCode
- *HTTP statusCode sent to the client e.g: 404
(not found)errorMessage
- the human-friendly error messagefor 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...)
Upvotes: 11
Reputation: 7550
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();
});
Upvotes: 6