I am looking to store information generated by a post-route in the session, pass it on to a get-route (where I redirect after the post is done), run res.render() with that information from the session and then clear it so the session-variable is empty for the next route (Code block explains problem as well if this was too vague). Please keep in mind that the code below is a simplified version, my program contains a working session in which I can store and access variables.
const clearSessionMessage = async (req, res, next) => {
await res.on('finish', () => {
let sess = req.session;
sess.message = '';
});
next();
};
app.use(clearSessionMessage);
app.get('/route', (req, res) => {
let sess = req.session;
res.render('template', { message: sess.message });
});
app.post('/post/route' (req, res) => {
let sess = req.session;
sess.message = 'The message I want to pass';
res.redirect('/route');
});
Possible solutions I have tried:
Change the 'finish' parameter in res.on() to: finish, end or close
Force the session to save with req.session.save(callback)
After some research I have been able to solve my problem.
It is possible to edit the session after calling res.render(); but if you wish to do that you have to make sure you force the session to save with req.session.save(callback). This is because express will automatically save the session after a res.render();, edits after the render are not automatically saved!
app.get('/route', (req, res) => {
res.render('template', { message: req.session.message });
req.session.message = undefined;
req.session.save(err => {
if (err) {
throw err;
};
});
});
app.post('/post/route' (req, res) => {
req.session.message = 'The message I want to pass';
res.redirect('/route');
});
You don't really need to save or modify the session after a res.render call but save a local variable before destroying the session, which is then passed to the rendered page:
app.get('/route', (req, res) => {
let msg = req.session.message;
req.session.message = undefined;
res.render('template', { message: msg });
});
app.post('/post/route' (req, res) => {
req.session.message = 'The message I want to pass';
res.redirect('/route');
});
Related
How to understand if somebody requests image or file in express? For example:
https://example.com/image.png
https://example.com/file.js
app.get("/:fileName", (req, res) => {
const file = req.params.fileName.split(".");
const fileExtension = file[file.length-1];
const imageExtensions = ["png", "jpg"];
if(imageExtensions.includes(fileExtension) {
console.log("It's an image");
} else if (fileExtension === "js") {
console.log("It's a javascript file");
}
return res.send();
});
But i would recommend to just separate the routes per resource type, for example:
app.get("/img/:fileName", (req, res) => {
console.log(`Getting image ${req.params.fileName}`);
return res.send();
});
app.get("/js/:fileName", (req, res) => {
console.log(`Getting JS file ${req.params.fileName}`);
return res.send();
});
Once the server received the request, you can check for the extension of the url and later process it. [need more information to know exactly what you need]
I'm having some problems using 2 middlewares inside the same function, already tried to search for all internet and didn't find a useful solution.
validator file
module.exports = {
create: async (req, res, next) => {
await celebrate(options.create)(req, res, next);
return res.status(500).json({ message: 'middleware 2'});
},
}
routes file
routes.post('/user', UserValidator.Create ,UserController.create);
The celebrate lib filters some basic validations like string lenght, null values, etc. And the celebrate() function returns another function with the (req, res, next) params.
When the celebrate returns the validation error, it stills continues to execute the code, so it tries to execute the next return and I get an error because the return has already been sent.
When using separate middlewares in the routes, it works normally:
routes.post('/user', celebrate(...), middleware2 ,UserController.create);
I also tried this way but the same thing happens, but now without an error, just returning the middleware2 result.
module.exports = {
create: async (req, res, next) => {
await celebrate(options.create)(req, res, () => {
return res.status(500).json({ message: 'middleware 2'});
});
},
Is there a way to fix this?
u should try this structure
// API
app.post('/something', Middleware.validate, Controller.create)
//Middleware
const validate = (req, res, done) => {
const errorArray = []
const body = req.body
// identifier is required, Validating as String, and length range.
if (!_.isString(body.identifier) || body.identifier.length < 2 || body.identifier.length > 10) {
errorArray.push({
field: 'identifier',
error: 70000,
message: 'Please provide only valid \'identifier\' as string, length must be between 2 and 10.'
})
}
if (!_.isEmpty(errorArray)) {
return errorArray
}
done()
}
module.exports = {
validate
}
// Controller
const create = function (req, res) {
return // your functionality
}
module.exports = {
create
}
I want to display rooms array like hypertext but after the first room is created it gives an error.
let rooms = [];
app.get("/rooms", function(req, res) {
for(let i=0;i<rooms.length;i++) {
res.send(''+rooms[i]+' <br>');
}
res.send(200)
});
app.get("/room/:roomName", function(req, res) {
let roomName = req.params.roomName;
rooms.push(roomName);
res.send(roomName);
});
Actually the problem is with res.send since it shows that server have responded to client. So you can try following code
app.get("/rooms", function(req, res) {
let responseString = "";
for(let i=0;i<rooms.length;i++) {
responseString += (''+rooms[i]+' <br>');
}
res.status(200).send(responseString)
});
I think the code below fixes your problem,
// Rooms must be const, didn't modified, push is enumerating not overriding.
const rooms = []
// Prefer arrow functions for better looking code
// If you don't use req, just _
app.get('/rooms', (_, res) => {
// Map over rooms,
const response = rooms.map(room => `${room}`)
res.send(response)
})
app.get('/room/:name', (req, res) => {
const { name } = req.params
rooms.push(name)
res.send(name)
})
app.get('/rooms', (req, res) => {
try {
return res.status(200).json(rooms.map(room =>(`${room}`)));
} catch(error) {
console.error(error);
return res.status(500).json(error); // or another status you decide.
}
})
I have myRoute.js with a route (GET) defined and I want to call an api endpoint from another route (api.js), and I'm not sure what the right way to do this is. The api.js route is working properly (image and code below).
api.js
router.get('/getGroups/:uid', function(req, res, next) {
let uid = req.params.uid;
db.getAllGroups(uid).then((data) => {
let response =[];
for (i in data) {
response.push(data[i].groupname);
}
res.status(200).send(response);
})
.catch(function (err) {
return err;
});
});
works as expected:
myRoute.js
I would like when a user goes to localhost:3000/USER_ID that the route definition gets information from the api. Psuedo code below (someFunction).
router.get('/:uid', function(req, res, next) {
let uid = req.params.uid;
let fromApi = someFunction(`localhost:3000/getAllGroups/${uid}`); // <--!!!
console.log(fromApi) ; //expecting array
res.render('./personal/index.jade', {fromApi JSON stringified});
});
Not sure if i understand you correct but anyway i will try to help. So you have an api like
router.get('/getGroups/:uid', function(req, res, next) {
let uid = req.params.uid;
db.getAllGroups(uid).then((data) => {
let response =[];
for (i in data) {
response.push(data[i].groupname);
}
res.status(200).send(response);
})
.catch(function (err) {
return err;
});
});
If you would like to reuse it you can extract a function from the code above like so:
async function getAllGroupsByUserId(uid){
const result = [];
try{
const data = await db.getAllGroups(uid);
for (i in data) {
result.push(data[i].groupname);
};
return result;
}
catch(e) {
return e;
}
}
And then reuse it in your api & anywhere you want:
router.get('/getGroups/:uid', async function(req, res, next) {
const uid = req.params.uid;
const groups = await getAllGroupsByUserId(uid);
res.status(200).send(groups);
})
Same you can do in your another route:
router.get('/:uid', async function(req, res, next) {
const uid = req.params.uid;
const fromApi = await getAllGroupsByUserId(uid); // <--!!!
console.log(fromApi) ; //expecting array
res.render('./personal/index.jade', {fromApi JSON stringified});
});
Seems like pretty clear :)
I would use fetch for this. You can replace someFunction with fetch, and then put the res.render code in a .then(). So, you would get this:
const fetch = require("node-fetch");
router.get('/:uid', function(req, res, next) {
let uid = req.params.uid;
fetch('localhost:3000/getAllGroups/${uid}').then(res => res.json()).then(function(data) {
returned = data.json();
console.log(returned); //expecting array
res.render('./personal/index.jade', {JSON.stringify(returned)});
});
});
A more robust way with error handling would be to write something like this:
const fetch = require("node-fetch");
function handleErrors(response) {
if(!response.ok) {
throw new Error("Request failed " + response.statusText);
}
return response;
}
router.get('/:uid', function(req, res, next) {
let uid = req.params.uid;
fetch('localhost:3000/getAllGroups/${uid}')
.then(handleErrors)
.then(res => res.json())
.then(function(data) {
console.log(data) ; //expecting array
res.render('./personal/index.jade', {JSON.stringify(data)});
})
.catch(function(err) {
// handle the error here
})
});
The ideal way would be to abstract your code into a method so you aren't calling yourself, as The Reason said. However, if you really want to call yourself, this will work.
I have the following function where I am using the cryptocompare npm package:
getPrice: function(coin){
cc.price(coin, 'USD')
.then(prices => {
console.log(prices);
return prices;
}).catch(console.error)
}
// https://github.com/markusdanek/crypto-api/blob/master/server/helper/cryptocompare.js
Now I want to set up an Express server to open http://localhost:9000/current and to display the current "price".
So I have my controller which looks like this:
module.exports = {
getCurrentPrice: function(req, res, next) {
getPrice('ETH', function(price);
}
};
// https://github.com/markusdanek/crypto-api/blob/master/server/controllers/CryptoController.jshttps://github.com/markusdanek/crypto-api/blob/master/server/controllers/CryptoController.js
My route:
var controllers = require('../controllers'),
app = require('express').Router();
module.exports = function(app) {
app.get('/current', controllers.crypto.getCurrentPrice);
};
When I open now http://localhost:9000/current I only get the current price in my console, but not in my browser.
How can I also set the response to the value?
I tried this but failed:
module.exports = {
getCurrentPrice: function(req, res, next) {
getPrice('ETH', function(price){
res.status(200).json(price);
});
}
};
I guess thats the wrong way to call a callback.. do I have to modify my helper function or anything else?
My project is also on Github for further references: https://github.com/markusdanek/crypto-api
below may help you
module.exports = {
getCurrentPrice: function(req, res, next) {
cc.price('ETH', 'USD')
.then(prices => {
console.log(prices);
res.json(prices)
})
.catch(err=>{
console.error(err)
return next(err);
})
}
};