In my app.js I'm passing on the request to the router like so
app.post('/validateUser', validationRoute)
In my route I'd normally do like this, to pass the request on to the controller, and call the appropriate function.
router.use('/', validationCon.validateUser)
My validateUser which is found in my controller, looks like this
module.exports = {
validateUser: async (req, res) => {
//const uName = req.body.uName,
//pwd = req.body.pwd;
const success = await logic.validateUser(uName, pwd);
if(success) {
//res.status(201).send('Login accepted');
res.render('../views/pages/secret')
}
else
res.status(400).send("Bad confidentials")
}
}
The issue I had by doing router.use('/', validationCon.validateUser) was that I didn't have the req and res to pass on to the controller, so these were undefined or null in my controller. So I changed my router to do:
router.use('/', (req, res, next) => {
//console.log("URL from validate: "+req.url, ' req.body: ', req.body)
username = req.body.username
pass = req.body.password
/* Now I want to pass these 2 values on to the controller*/
})
module.exports = router
Now I have the req and res objects in my route, and so I can access the values in the request body. But the issue I have now, is that I don't know how to pass these on to the controller, since I no longer do router.use('/', validationCon.validateUser)
How can I do so that I can access the req body in the controller? As you can see in the controller, I am also passing the values from the request body on to the model, so that they can be verified in the database.
I am not sure if this is the best approach of doing it, this is my first nodejs/express application, so I'm all ears if there are suggestions. Thanks
The req, res, next should be available at the controller too.
A better approach is to set the app to use routes, and specify post \ get at the route level.
See here
Related
I am trying to redirect the user with a post request from the home page after checking if their sessions exist.
This is my home controller file:-
const express = require('express');
const router = express.Router();
router.get('/', (req, res, next) => {
if (req.session["Data"] != undefined) {
res.redirect(307, '/Try');
}
else {res.render('home', {pageTitle: "Home"});}
});
module.exports = router;
But it is giving me error- Cannot GET /Try
This is what I'm using in my route file- router.post('/Try', try_controller.Try);
I am using res.redirect(307, '/Try') in another controller file of the same project and it's working. I can't figure out why it's not working here.
I don't think you can redirect a GET as a POST. If you own the /Try route, one option is to add a GET handler for that, then redirect will work.
Otherwise, in your GET route handler for \ you can create a new POST and return the results of that.
const request = require('request')
router.get('/', (req, res, next) => {
if (req.session["Data"] != undefined) {
//res.redirect(307, '/Try');
request.post('/Try', {}, function(err, response, body) {
if (err) return next(err)
return res.status(response.statusCode).send(body);
})
}
else {res.render('home', {pageTitle: "Home"});}
});
The example above an https://github.com/request/request though there are more modern ways of sending POST from express.
This isn't technically "redirecting", so you won't return 307 or 302.
I tried different things but in the end, I added an empty form in my home.pug file and submitted it using js.
JS code -
script.
let ssn = !{JSON.stringify(session)};
data = "Data"
if (ssn[data] != undefined) {document.getElementById('form-id').submit();}
I have a Node Express web project using Pug views.
By using the Response.locals object, I can make the Express Request object (req in my code) available to every pug file:
const app = require("express")();
app.use((req, res, next) => {
res.locals.req = req;
next();
});
Are there any side effects of using this approach and what are the disadvantages?
The convenience I get is that any view file can have access to all properties of the Request object, eg the query string, etc, without having to pass them explicitly using the textbook method like:
app.get("/xx", (req, res) => {
res.render("xx", { query: req.query });
});
Are there any side effects of using this and what are the disadvantages? Does it use up a lot of memory?
There are no side effects of using a template engine with express. Actually you can use res.render instead.
The convenience I get is that view files can have access to the query string,
If we know req.query has what we need that leaves the question is How to resolve the template for each route?
We will need to split the solution into two parts.
Part One - Genital request handle.
Although the response is dynamic the request resolving is known. Let say we have an additional parameter on the req object req.template and as we sad req.query is available as well.
The below function will render the req.query into req.template and send the response.
function pugTemplateHandler(req, res) {
const compiledFunction = pug.compileFile(req.template);
res.send(compiledFunction(req.query));
}
We don't care about Method nor Routes here. We expect that req to be set before this is called.
Part Two - Resolve the template file according to the route and method
Above we promised pugTemplateHandler that req will be ready for it. We can use Middleware to set the members we need on the req object.
app.get('...', (req, res, next) => {
req.template = 'PUG_TEAMPLATE_PAT'; // resolve template
// req.query = { ... }; // add or overwrite use params
next();
}, pugTemplateHandler); // pass modified req
app.post('...', (req, res, next) => { ... }, pugTemplateHandler);
app.put('...', (req, res, next) => { ... }, pugTemplateHandler);
app.del('...', (req, res, next) => { ... }, pugTemplateHandler);
Because we know the HTTP Method and the Route resolving the template is easy. Most likely here the template will be a static string.
The solution is extendable and has respect the idea of separation of concerns.
this is my router
router.post('/hello', (req, res) => {
const name = req.body.name;
res.send(welcome(name));
});
and this is my welcome function
function welcome(name) {
console.log(name)
// create cookie here
}
how can I create a cookie in the welcome function should I pass req and res along the name to that function?
is this the right approach? thank you
From technical point of view yes you need to set the cookie on the res object so, the res object should be passed where you need it!
Pass req and res (and eventually next) is the right approach because is express middleware friendly Express middleware docs
Need to use cookieParser().
app.use(express.cookieParser());
Then you need to set the cookies example below
function welcome(name) {
//set cookies
res.cookie('test', 'Welcome data');
}
Then get the cookies in separate router
router.get('/cookies', (req, res) => {
//get cookies
res.send(req.cookies.test);
});
From what I have read here and here, the order in which you place your middleware function matters, as you can have certain routes not go through the middleware function if it is placed before the route, and the routes which are placed after will go through this middleware function.
I am seeing mixed results as my dev environment is not respecting this and my prod environment is. The code is exactly the same.
What I am trying to do is have my login route not be protected by a token checker middleware function and have the rest of my routes protected by a token.
Here is my code:
routes.get('/login', function(req, res) {
// login user, get token
});
routes.use(function(req, res, next) {
// check header or url parameters or post parameters for token
var token = req.headers['access-token'];
// decode token
if (token) {
// validate token
}
else if (req.method === 'OPTIONS') {
next();
}
else {
// if there is no token
// return an error
return res.status(403).send({
success: false,
message: 'No token provided.'
});
}
});
routes.get('/query/:keywords', function(req, res) {
console.log(req.params.keywords);
// execute query
});
app.use('/', routes);
the /query route is the only one that should have to go through the token middleware function correct? Right now I am getting the /login route also going through the token middleware function, which doesn't make sense as I shouldn't need to have a token to login.
Better yet, if there is a way to target which routes I want protected and which routes I do not want protected, this seems better than having to rely on an "order" of where the middleware function is placed.
First, follow along this usage in ExpressJS:
More than one callback function can handle a route (make sure you specify the next object). For example:
app.get('/example/b', function (req, res, next) {
console.log('the response will be sent by the next function ...')
next()
}, function (req, res) {
res.send('Hello from B!')
})
You'll notice it's definition is close to what you're declaring on routes.use(yourFunction(...)). However, there's no real reason to do it this way other than following examples you've seen in documentation, which is a good way to start nevertheless.
However, it's a flimsy implementation, express will allow hierarchies within it's .get() .post() methods, that's correct, but this is a use case specific and not what you're looking for.
What you need is to implement your custom auth process using the double callback configuration. do this:
// You can save this function in a separate file and import it with require() if you want
const tokenCheck = function(req, res, next) {
// check header or url parameters or post parameters for token
var token = req.headers['access-token'];
// decode token
if (token) {
// validate token
}
else if (req.method === 'OPTIONS') {
next();
}
else {
// if there is no token
// return an error
return res.status(403).send({
success: false,
message: 'No token provided.'
});
}
});
routes.get('/login', function(req, res) {
// login user, get token [Unprotected]
});
routes.get('/query/:keywords', tokenCheck, function(req, res) {
console.log(req.params.keywords);
// execute query [Protected with tokenCheck]
});
app.use('/', routes);
You might need to play around with the code above, but it'll guide you on the right direction, this way, you can specify particular routes to execute the tokenCheck(req, res, next) function as you want.
The easiest way to do this is to use Router Middleware to scope Routes that require Authentication and the routes that don't. Since all Routers are Middleware, we can implement them just like any other middleware. Ensuring that we place the Routers and Routes in the order that we would like our Routes to be evaluated.
In the below example, the Express server has 2 routers, a LoginRouter and an ApiRouter.
LoginRouter - Generates a Token when receiving a request to POST /login and returns that to the requester for subsequent use in the /api routes.
ApiRouter - Wraps all other routers, centralizes middleware that needs to be globally applied to all routes under /api. Is only accessible to Authenticated Requests.
The API Router is only accessible if there is a token included in the Header and that token is obtained from the LoginRouter. LoginRouter has no authentication required.
With this setup, you'll keep adding routers after the Authorization Middleware to the API Router via .use() on the ApiRouter.
The below pattern of composing Routers from other Routers is very powerful, scalable and easy to maintain.
server.js
const express = require('express')
const bodyParser = require('bodyParser')
const ApiRouter = require('./routes/api')
const LoginRouter = require('./routes/login')
const port = process.env.PORT || 1337
const server = express()
server.use(bodyParser.json())
server.use('/login', LoginRouter)
server.use('/api', ApiRouter)
server.listen(port, () => console.log(`Listening on ${port}`))
LoginRouter - /routes/login.js
const router = require('express').Router()
router.post('/', (req, res) => {
// Validate Credentials
// some validation code...
// Then create the token for use later in our API
let token = '...'
// Response 200 OK with the token in the message body
return res.status(200).send({token})
})
module.exports = router
ApiRouter - /routes/api/index.js
const router = require('express').Router()
const UsersRouter = require('./routes/api/users')
router.use((req, res, next) => {
let authorizationHeader = req.headers['authorization'] || req.headers['Authorization'] // handle lowercase
let [, token] = authorizationHeader.split(' ')
if (!token) {
return res.sendStatus(403) // Forbidden, you're not logged in
} else {
// validate the token
if (!tokenIsValid) {
return res.sendStatus(403) // Forbidden, invalid token
}
// Everything is good, continue to the next middleware
return next()
}
})
router.use('/users', UsersRouter)
module.exports = router
UsersRouter - /routes/api/users
const router = require('express').Router()
router.get('/', (req, res) => {
// We only get here if the user is logged in
return res.status(200).json({users: []})
})
module.exports = router
The application of the token middleware should not happen to the login route due to route order and the fact the login route never calls the next object. Without more information we really can't trouble shoot what is happening beyond that however you could try inspecting it in your dev environment with a debugger break and looking at the req that hits that middleware.
We can however give you some information on how to try and isolate your .use middleware and how application of middleware order applies so that you can try and separate it from the login route entirely like in the bottom of your question.
When applying middleware to only specific routes you should keep note that order and .use are for middleware that should answer the request before telling express to continue looking for other middleware that come after them in the router that will also handle the request. If you only want it on a few routes, you can add it to only a few routes by being explicit like so:
router.get('/route', [ middleware1, middleware2, ..., middlewareX])
or
router.get('/route', middleware1, middleware2, ..., middlewareX)
both patterns will work. I however find the array pattern a little more palatable since I can define a lot of middle wares I want to apply and then concatenate new middleware for specific logic, and I only need modify where I declare that concatenation to add more functionality. It'd however rare to need that many middleware and you should be able to use either.
You could also section that middleware off to a subset of routes by using a router and applying it as the first middleware to the route chain before the router.
app.use('/user', authentication, userRouter)
or you can put it inside the router as the first middleware with a .use so that it handles all requests.
So remember the general tips about middleware usage:
order matters for middleware application
optional middleware that should be applied on route basis should be applied with the other middleware in order for only that route
error handling middleware must always come last, and have four arguments (err, req, res, next)
use routers to section .use middleware to specific routes and sets of routes
You can find more information about it in the expressjs documentation for middleware
I have build an api using express. In my routes file I have:
app.route('/getBalances')
.post(api.getBalances);
api.getBalances, depending on a parameter send through post called "vehicle" gets first which is the correct controller to load and to invoke its getBalances method, in example:
var controller = commonModel.getController(query.vehicle.toLowerCase());
controller.getBalances();
getBalances is not the only entry point I have, so I was wondering if it was possible to call a "global" method which is call for every entry point, in that way I wouldn't need to identify the correct controller on each method but on the global method.
Thanks in advance for your help.
Use a preliminary middleware which will run before adding any api route. Example:
// This middleware has to be added first.
app.use(function(req, res, next) {
var query = req.query; // or `req.body`, whatever you like
if (query && query.vehicle) {
req.controller = commonModel.getController(query.vehicle.toLowerCase());
}
next(); // delegate request to the next routes
});
// Now add specific api middlewares.
app.route('/getBalances')
.post(function(req, res) {
var controller = req.controller; // we've populated this earlier
res.send(controller.getBalances());
});
app.route('/anotherMethod')
.post(function(req, res) {
var controller = req.controller;
// etc.
});