I would like to display my error message in my jade view after validation.
Jade
H1 Hello world
#error
p #{JSON.stringify(errorMsg)}
div
form(action="/",method="post")
div Foo
div
input(type="text",name="foo")
div
input(type="submit",name="submit")
This is my server code...
server.register(Vision,(err)=>{
if(err){console.log(err)}
server.views({
engines:{
jade:Jade
},
path: 'views',
relativeTo: __dirname });
server.route([
{
method:'GET',path:'/',
handler:(request,reply)=>{
reply.view('index');
}
},
{
method:'POST',path:'/',
handler: (request,reply) => {
console.log("POST '/' ");
const err = {}; // check error and set it
reply.view('index',{errorMsg:err});
},
config:{
validate:{
payload: {
foo: Joi.string().required(),
submit:Joi.string().required()
}
}
}
}
]); });
server.start((err)=>{
if(err){console.log(err);} console.log("server started:"+server.info.uri);
});
Goal is to validate the presence of foo
When the validation kicks in the server responses with a http 400 error which is totally fine and expected for api's. This happens even before the handler function is called.
What is the best way to handle validation error with using a view engine?
I was expecting something like (which is obviously not working)
if(request.error){
reply.view('index',{errorMsg:err});
}
I saw also some answers who dealed with the onPreResponse event to catch it globally. Isn't their a way to do this in the request handler method?
Or any best practise tips?
The docs cover this situation specifically. See the specifics here.
The short version taken directly from the docs looks like this:
const preResponse = function (request, reply) {
const response = request.response;
if (!response.isBoom) {
return reply.continue();
}
// Replace error with friendly HTML
const error = response;
const ctx = {
message: (error.output.statusCode === 404 ? 'page not found' : 'something went wrong')
};
return reply.view('error', ctx);
};
server.ext('onPreResponse', preResponse);
Basically on the preResponse event, check if the response is a boom object. If not, reply normally. If is is a boom object reply with your error page and pass some data into it.
If you are using a JOI object to validate the payload you cannot handle it in the handler.
If you log out the request lifecycle:
server.route({
method: 'POST',
path: '/',
handler: (req, reply) => {
reply('hello');
console.log('handler');
},
config: {
validate: {
payload: {
date: Joi.date().required(),
},
},
},
});
server.ext('onRequest', (request, reply) => {
console.log('onRequest');
reply.continue();
});
server.ext('onPreAuth', (request, reply) => {
console.log('onPreAuth');
reply.continue();
});
server.ext('onPostAuth', (request, reply) => {
console.log('onPostAuth');
reply.continue();
});
server.ext('onPreHandler', (request, reply) => {
console.log('onPreHandler');
reply.continue();
});
server.ext('onPostHandler', (request, reply) => {
console.log('onPostHandler');
reply.continue();
});
server.ext('onPreResponse', (request, reply) => {
console.log('onPreResponse');
reply.continue();
});
And try a valid "date" parameter you will get:
onRequest
onPreAuth
onPostAuth
onPreHandler
handler
onPostHandler
onPreResponse
When you try an invalid parameter, so the validation fails:
onRequest
onPreAuth
onPostAuth
onPreResponse
As you can see the handler is not called at all so you cannot handle it the way you described.
If you don't want to implement the routes as an API and then consume it by your site, I would recommend removing the validate attribute from your config and use Joi.validate() in you handler like so:
server.route({
method: 'POST',
path: '/',
handler: (req, reply) => {
Joi.validate(req.payload, dateSchema, (err, val) => {
if (err) {
reply.view('index', { error: err });
}
reply('the date is valid');
});
},
});
Remember that Joi is throwing an error object so it's not just text.
Related
I have the following json body parser middleware, which is to parse the request body based on the option provided in the parameter.
enum BodyParserErrorType {
ENTITY_PARSE_FAILED = 'entity.parse.failed',
}
const jsonBodyParser = function (options?: OptionsJson) {
return (req: Request, _res: Response, next: NextFunction): void => {
express.json(options)(req, _res, err => {
if (err) {
if (err.type === BodyParserErrorType.ENTITY_PARSE_FAILED) {
next(new JSONParsingError(ERROR_CODE.INVALID_REQUEST));
}
}
next();
});
};
};
// sample usage:
jsonBodyParse({limit: '5mb'});
I wrote the following jest test for it:
describe('Parse request body', () => {
const mockRequest: Partial<Request> = {
body: '{"mockBody":"12345"}',
};
const mockResponse: Partial<Response> = {};
const nextFunction: NextFunction = jest.fn();
test('Should successful parse request body to json', () => {
const middleware = ParseMiddleware({ limit: '5mb' });
middleware(mockRequest as Request, mockResponse as Response, nextFunction);
expect(nextFunction).toBeCalledTimes(1);
expect(nextFunction).toBeCalledWith();
});
});
The above code will generate the error:
TypeError: Cannot read property 'transfer-encoding' of undefined
around the line of express.json(options)(req. _res, error) {...})
I can't even find transfer-encoding via a search through the node_modules folder, but I guess I missed something in the mockRequest but don't know how to fix it.
I'm making a function that permits me to upload a picture to imgur in my express api (nodejs),
i'm encoutering an error when calling a function returning a promise:
TypeError: res.status is not a function
at uploadpicture.then
This is my code:
Where error is raised:
router.post('/upload', (req, res, next)=> {
var busboy = new Busboy({headers: req.headers});
busboy.on('file', function(fieldname, file, filename, encoding, mimetype) {
if(fieldname == 'image') {
// the buffer
file.fileRead = [];
file.on('data', function(data) {
// add to the buffer as data comes in
this.fileRead.push(data);
});
file.on('end', function() {
// create a new stream with our buffered data
var finalBuffer = Buffer.concat(this.fileRead);
upload = uploadpicture(finalBuffer).then((res)=>{ //success request
console.log(res);
res.status(200).json({success: true, message: "Successfully uploaded !", url: res.data.link});
},(err)=>{ //error
res.status(500).json({success: false, message: "Error happenned while uploading !"});
}).catch((error)=>{
console.log(error);
res.status(500).json({success: false, message: "Error happenned while uploading !"});
});
})
}
});
busboy.on('finish', function() {
//busboy finished
});
req.pipe(busboy);
});
And the function :
function uploadpicture(stream){ //get picture stream
return new Promise((resolve, reject)=>{
var options = {
uri: 'https://api.imgur.com/3/image',
method: 'POST',
headers: {
//'Authorization': 'Client-ID ' + config.client_id_imgur // put client id here
},
formData: {
image: stream,
type: 'file'
},
auth: {
bearer: config.access_token_imgur,
}
};
request(options)
.then((parsedBody)=> {
resolve(parsedBody);
})
.catch((err)=> {
console.log(err);
reject(err.toString())
});
});
}
The code works perfectly, but i don't know why suddendly this error happened,
i tried to :
change arrow functions to function(){}
Add next to the route parameters
Nothing worked, Thanks for your help
The accepted answer directly addresses the OP's problem, but I post another solution since you can also encounter this error in other places.
When you have:
api.use((error: ErrorRequestHandler, request: ExpressRequest, response: ExpressResponse) => {
response.status(500).end() // response.status is not a function
})
Because the error handling route must accept 4 arguments for express to identify it as an error middleware.
api.use((error: ErrorRequestHandler, request: ExpressRequest, response: ExpressResponse, next: NextFunction) => {
response.status(500).end()
})
Just adding the next function (or whatever argument you're missing) will fix it.
https://github.com/visionmedia/supertest/issues/416#issuecomment-514508137
At this point:
upload = uploadpicture(finalBuffer).then((res)=>{ //success request
the resis the result of promise uploadpicture function (that is the parsedBody), not the res from the express route. So indeed, it has no status function. Try change the then callback name like:
upload = uploadpicture(finalBuffer).then((otherName)=>{ //success request
You are getting this error:
TypeError: res.status is not a function
Because the order should be (err, res, req, next) not (req, res, err, next),
example below
const errorHandler = (err, req, res, next) => {
const statusCode = res.statusCode === 200 ? 500 : res.statusCode;
res.status(statusCode)
res.json({
message : err.message,
stack :process.env.NODE_ENV === 'production' ? null : err.stack,
})
}
Order of parameters really matters i had error in below code
const getImagesByBrand = async (res) => {
try {
const images = await Image.find();
res.status(200).json(images);
} catch (error) {
res.status(500).json(error);
}
};
I was not giving req as parameter and that was the reason for error i just add req,res and it worked
If you are using the async/await method:
const notifications = await notifications.aggregate({...})
if(notifications){
return res.status(200).json({ data: notifications })
}else{
return res.status(404).json({ message: 'No notifications found'})
}
Make sure that you are including your return statements. Not including a return statement will cause this. Something else that I was doing is I had JSON instead of json, which will most definitely throw an error.
I am calling 2 API request 1 after another so I decided to use the waterfall model but I am facing the issue in it
I have tried so much but not able to solve the issue.
Below is my code:
var unirest = require("unirest");
var async = require("async")
exports.user = (req, res, next) => {
const qry = req.params.id
async.waterfall([
(nextCall) => {
var req = unirest("GET", API_URL1);
req.query({
// some query
});
req.headers({
// some headers
});
req.end(function(subCount) {
// if (resp.error) throw new Error(resp.error);
var channelSubCount = subCount.body
nextCall(null, data)
});
},
(data, nextCall => {
console.log(channelSubCount, 'data')
var reqs = unirest("GET", API_URL2);
reqs.query({
// some query
});
reqs.headers({
// some headers
});
reqs.end(function(res) {
// if (res.error) throw new Error(res.error);
console.log(res.body);
return nextCall(null, {
name: 'abc',
photo: 'src',
count: data
})
});
})
], function(finalData) {
// if (error) { alert('Something is wrong!'); }
console.log('final')
res.status(200).json(
finalData
);
});
};
ERROR:
Reference Error: data is not defined
I don't understand why this is happening.
Also some please show me the right way to implement the above things with optimizations.
Any help appreciated...
Looks like you forgot to close parentheses here in your second arrow function definition:
(data, nextCall => {
It's still a valid JavaScript, but the interpreter now treats data not as a function incoming parameter (as you need), but as a variable. But it's not defined anywhere, therefore you have that error.
Make it like this and it will work:
(data, nextCall) => {
I tried this https://github.com/hapijs/hapi/issues/800. This didn't work for me.
const start = async () => {
await server.register(require('inert'));
server.route({
method: 'GET',
path: '/samplespa/{file*}',
handler: function (request, h) {
directory :{
path : './samplespa/'
listing: true
}
}
});
server.ext('onPostHandler', (request, reply) => {
console.log('WORD');
const response = request.response;
if (response.isBoom && (response.output.statusCode === 500) ) {
return reply.file('./samplemap.html');
}
return reply.continue;
});
await server.start();
console.log('Server running at:', server.info.uri);
};
start();
I want to server the directory samplespa and render the file "index.html" in it and note that index.html is written in Angular 1.X and depends on files in the directory..
Also for the path : http://localhost:8000/samplespa/index.html
I get the following response
{
"statusCode": 500,
"error": "Internal Server Error",
"message": "An internal server error occurred"
}
I get the following error msg in vs code:
Debug: internal, implementation, error
Error: handler method did not return a value, a promise, or throw an error
at module.exports.internals.Manager.execute (/Users/pavithran/projects/toilet-tracker/node_modules/hapi/lib/toolkit.js:52:29)
at <anonymous>
at process._tickCallback (internal/process/next_tick.js:188:7)
How to do this??? I have tried everything for the past 2 days and not able to figure it out..
For single file you can use this:
server.route({
method: 'GET',
path: '/samplespa/index.html',
handler: function (request, h) {
return h.file('./samplespa/index.html');
}
});
For multiple files, the following. Note that the handler is now not a function but an object.
server.route({
method: 'GET',
path: '/samplespa/{file*}',
handler: {
directory: {
path: 'samplespa'
}
},
});
I am trying to use hapi-auth-bearer-simple module to enable bearer token on my app. However, I am getting the error shown in the title.
I am trying to implement this module to enable token authorisation in my app But I am getting error mentioned below
e:\python_training\Training\Node\Test\Project\Backend\node_modules\hapi\node_modules\hoek\lib\index.js:723
I have a route file
module.exports = [
{
method: 'GET',
path: '/api/{_id?}',
handler: function (request, reply) {
Controller.control.get(request.params, function (err, success) {
console.log(request.params);
if (err) {
reply(unifunc.sendError(err));
} else {
reply(unifunc.sendSuccess(SuccessMsg,success)).code(200);
}
});
},
config: {
description: 'desc',
tags: ['api', 'oV'],
validate: {
headers: unifunc.authorizationHeaderObj,
params: {
o_id: Joi.string().required().trim(),
_id: Joi.string().optional().trim()
},
failAction: unifunc.failActionFunction
},
auth: {
strategy: 'bearer',
scope: ['admin', 'user-{params.id}']
},
plugins: {
'hapi-swagger': {
responseMessages: msgs
}](url)
and a controller file in which I mentioned strategy
var bearerSimple= require('hapi-auth-bearer-simple')
authorization = Authorization.auth; // This plugin has the logic to validate the token and return the error in case it fails and I am passing accesstoken as parameter in a function in that file
var getV = function(server, params, callbackRoute){
server.register(
[{
register: bearerSimple
}], function(err){
if(err){
console.log("Failed to log the plugin",err);
throw err;
}
server.auth.strategy('bearer', 'bearerAuth', {
authorization : authorization
});
});
console.log(params);
async.series([
function(cb){}
]}
complete error message is:
Error: Unknown authentication strategy: bearer in path: /api/orders/{order_id}/vehicles/{_id?}
at Object.exports.assert (e:\python_training\Training\Node\Test\Project\Backend\node_modules\hapi\node_modules\hoek\lib\index.js:723:11)
at e:\python_training\Training\Node\Test\Project\Backend\node_modules\hapi\lib\auth.js:152:14
at Array.forEach (native)
at internals.Auth._setupRoute (e:\python_training\Training\Node\Test\Project\Backend\node_modules\hapi\lib\auth.js:149:24)
at new module.exports.internals.Route (e:\python_training\Training\Node\Test\Project\Backend\node_modules\hapi\lib\route.js:142:47)
at internals.Connection._addRoute (e:\python_training\Training\Node\Test\Project\Backend\node_modules\hapi\lib\connection.js:375:17)
at internals.Connection._route (e:\python_training\Training\Node\Test\Project\Backend\node_modules\hapi\lib\connection.js:367:18)
at wrappedRoute [as _route] (e:\python_training\Training\Node\Test\Project\Backend\node_modules\newrelic\lib\instrumentation\hapi.js:222:29)
at internals.Plugin._apply (e:\python_training\Training\Node\Test\Project\Backend\node_modules\hapi\lib\plugin.js:460:14)
at internals.Plugin.route
Is there any way I can resolve this issue?
Edit:
I modified server.js file and removed the strategy from controller file
I placed strategy in server.js
var validationFunction = Authorization.auth;
console.log(validationFunction);
server.register(
[{
register: bearerSimple
}], function(err){
if(err){
console.log("Failed to log the plugin",err);
throw err;
}
server.auth.strategy('bearer', 'bearerAuth', {
validationFunction : validationFunction
});
});
and in Authorization file looks like this
function rauth(accessToken, cb) {
var criteria = {accessToken: accessToken};
var projection = {};
var options = {limit: 1};
Service.AdminService.getadmin(criteria, projection, options, function (err, data) {
if (err) {
cb(err);
} else if (data && data.length > 0 && data[0]._id) {
console.log(data);
console.log(data.length);
adminId = data[0]._id;
cb()
} else {
cb(UniversalFunctions.CONFIG.APP_CONSTANTS.STATUS_MSG.ERROR.INVALID_ACCESS_TOKEN);
}
});
Now I am getting this error:
Error: options.validateFunc must be a valid function in bearerAuthentication scheme
I have been breaking my head over this problem from days. Could anyone suggest what could be the problem here?
The only problem I found was with the parameters of callback function passed in validateFunction but I can't remove the parameters as those parameters are being defined in another function called getadmin. Could anyone suggest a workaround for this?
Solved in this issue https://github.com/Salesflare/hapi-auth-bearer-simple/issues/69.
The problems were a typo and needed to pass more info back on a successful authorization.