How to set path for single page application in Hapi js - javascript

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'
}
},
});

Related

TypeError: res.status is not a function

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.

Error: Response not received using IBMCloud VisualRecognition NodeJS

I'm follow the getting started's IBMCloud Visual Recognition page: https://cloud.ibm.com/docs/services/visual-recognition?topic=visual-recognition-getting-started-tutorial&programming_language=javascript#getting-started-tutorial.
The follow code is presented:
var VisualRecognitionV3 = require('watson-developer-cloud/visual-recognition/v3');
var fs = require('fs');
var visualRecognition = new VisualRecognitionV3 ({
version: '2018-03-19', iam_apikey: '{apikey}'
});
var url= 'https://watson-developer-cloud.github.io/doc-tutorial-downloads/visual-recognition/640px-IBM_VGA_90X8941_on_PS55.jpg';
var params = {
url: url,
};
visualRecognition.classify (params, function (err, response) {
if (err) {
console.log(err);
} else {
console.log(JSON.stringify(response, null, 2))
}
});
When I execute it via node on Windows I get the follow error message:
error: { Error: Response not received. Body of error is HTTP ClientReque
t
at formatError (C:\Users\augusto\node_modules\ibm-cloud-sdk-c
requestwrapper.js:111:17)
at C:\Users\augusto\node_modules\ibm-cloud-sdk-core\lib\reque
r.js:259:19
at process._tickCallback (internal/process/next_tick.js:68:7)
message:
'Response not received. Body of error is HTTP ClientRequest object',
I've installed alright like the tutorial and I don't know where are the error.
Solved using:
const visualRecognition = new VisualRecognitionV3({
version: '{version}',
iam_apikey: '{apikey}'
disable_ssl_verification: true,
});

Unknown authentication strategy: hapi-auth-bearer-simple

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.

hapijs with jade view engine best way to display errors

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.

Node.JS crashes with "cant set headers after they are sent" error

I have a Node.JS based server. One of the functions of the server is to receive a photo from a mobile client and store it in the mongodb. When there is a bad connection, the file upload process fails, the client receives an error and retries, and everything works as expected, but sometimes, after the upload fails, the server crashes with the "Can't set headers after they are sent" error. Below is the error generated by the server: the user is authenticated and then the upload process fails, PUT /v1/add_photo.json command returns error code 400, but it seems that the remaining functions of the add_photo.json are still called - why? and thus, this error is generated, since at the very end, the send_failure function will set the header again. (When a "successful" upload fail occurs - after add_photo.json returns error code 400, no other functions are called, and everything works as expected)
3/23/2014 16:6:41 0.0.2 handlers/users.js: authenticate_user:
Error: Request aborted
at IncomingMessage <anonymous
(/floomit/node/node/node_modules/express/node_modules/connect/node_modules/multiparty/index.js:93:17)
at IncomingMessage.EventEmitter.emit (events.js:92:17)
at abortIncoming (http.js:1911:11)
at Socket.socket.onend (http.js:2010:7)
at Socket.g (events.js:175:14)
at Socket.EventEmitter.emit (events.js:117:20)
at _stream_readable.js:920:16
at process._tickCallback (node.js:415:13)
PUT /v1/add_photo.json 400 9542ms - 467b
3/23/2014 16:6:41 0.0.2 handlers/streams.js: add_photo:
3/23/2014 16:6:41 0.0.2 data/streams.js: store_file:
3/23/2014 16:6:41 0.0.2 data/streams: store_file: err:
{ [Error: ENOENT, open '/tmp/4684-1425605.jpg'] errno: 34, code: 'ENOENT', path:'/tmp/4684-1425605.jpg' }
3/23/2014 16:6:41 0.0.2 handlers/streams.js: add_photo: err
{ [Error: ENOENT, open '/tmp/4684-1425605.jpg'] errno: 34, code: 'ENOENT', path:'/tmp/4684-1425605.jpg' }
http.js:733
throw new Error('Can\'t render headers after they are sent to the client.'
^
Error: Can't render headers after they are sent to the client.
at ServerResponse.OutgoingMessage._renderHeaders (http.js:733:11)
at ServerResponse.res._renderHeaders
(/floomit/node/node/node_modules/express/node_modules/connect/lib/patch.js:69:27)
at ServerResponse.writeHead (http.js:1150:20)
at ServerResponse.res.writeHead
(/floomit/node/node/node_modules/express/node_modules/connect/lib/patch.js:75:22)
at Object.exports.send_failure (/floomit/node/node/handlers/helpers.js:78:9)
at /floomit/node/node/handlers/streams.js:377:21
at /floomit/node/node/node_modules/async/lib/async.js:428:21
at /floomit/node/node/data/streams.js:495:13
at /floomit/node/node/node_modules/mongodb/lib/mongodb/gridfs/gridstore.js:294:22
at Object.oncomplete (fs.js:107:15)
As can be seen from the log lines above (the lines with date preceding them are printed by my server), the function add_photo is entered, it calls a function store_file, which fails. It seems that the failure happens because the file wasn't successfully uploaded to the server, as indicated by the ENOENT error. Which should just result in an error code being returned to the client and the client retransmitting, but instead the server crashes. This doesn't happen all the time, most of the time the client is returned an error code and retransmits. I event tried modifying the store_file() function to always return the above generated ENOENT error and no crashes occur, the client simply retransmits.
Below is the relevant code from the server:
// stream handler
exports.add_photo = function (req, res) {
var new_photo = new Photo();
async.waterfall([
// store image file
function (cb) {
stream_data.store_file(req.files.file_image.path, cb);
},
function (imageFileId, cb) {
new_photo.imageFileId = imageFileId;
stream_data.add_photo(new_photo, cb);
}
],
function (err, final_photo) {
if (err) {
helpers.floomit_log("handlers/streams.js: add_photo: err");
helpers.floomit_log(err);
helpers.send_failure(res, err);
} else {
helpers.send_success(res, final_photo.response_obj());
}
});
};
// stream_data
exports.store_file = function (file_full_path, callback) {
async.waterfall([
function (cb) {
fileId = db.ObjectID();
fileGS = new db.GridStore(db.db, fileId, "w", {"chunk_size":1024*4});
fileGS.writeFile(file_full_path, cb);
}
],
function (err, results) {
if (err) {
helpers.floomit_log("data/streams: store_file: err:");
helpers.floomit_log(err);
callback(err);
} else {
callback(null, fileId);
}
});
};
// stream_data
exports.add_photo = function (new_photo, callback) {
var query = {};
query["$or"] = [];
async.waterfall([
function (cb) {
new_photo._id = db.ObjectID();
db.photos.insert(new_photo, {w:1}, cb);
},
function (result, cb) {
for (i = 0; i < new_photo.streams.length; i++) {
query["$or"].push({"user":new_photo.streams[i].user,"name":new_photo.streams[i].name});
}
db.streams.update(
query,
{"$inc" : {"photoCount":1}, "$set" : {"timestampU":new_photo.timestamp}},
{w:1, multi:true},
cb);
}
],
function (err) {
if (err) {
helpers.floomit_log("data/streams.js: add_photo: err:");
helpers.floomit_log(err);
callback(err);
} else {
callback(null, new_photo);
}
});
};
// helpers
exports.send_success = function(res, data) {
res.writeHead(200, {"Content-Type": "application/json"});
var output = { error: "none", data: data };
res.end(JSON.stringify(output) + "\n");
};
// helpers
exports.send_failure = function(res, err) {
var code = (err.code) ? err.code : err.name;
res.writeHead(code, { "Content-Type" : "application/json" });
res.end(JSON.stringify({ error: code, message: err.message }) + "\n");
}
The send_failure function does not return proper HTTP error when this occurs, I need to fix this, but that's not where the problem is. (just wanted to point it out so that this issue is not brought up here)
Thank you,
Gary

Categories