I'm trying to improve the security of my express app by using the express-validator package, I figured out that I could use it in two different ways.
The first inside a middleware:
const {check}=require("express-validator");
app.post(
"/random-post",
[
check("email").isEmail(),
],
(req, res) => {
//some code
})
Second inside of the router's call back:
app.post("/random-post",(req,res,next)=>{
check(req.body.email).isEmail()
})
Which of the two are most used or maybe should I use both?
I think the best practice here is to do the validation and sanitazion before reaching the controller. In the same block of logic you can also check if there are any validation errors and return an error. This way the controller is never reached, so code in your controller is never executed. I updated your example below:
const {check} = require('express-validator');
app.post(
'/random-post', [
check('email').isEmail(),
(req, res, next) => {
const errors = validationResult(req);
if (!errors.isEmpty()) {
return res.status(422).json({
errors: errors.array()
});
}
next();
}
],
(req, res) => {
//some code in your controller
}
);
Related
I upgraded to Express 4 and have the following problem with error handling.
Before I used to have the code in app.js — after all the possible routes I had
var routes = require('./routes')
app.use(routes.notfound)
app.use(routes.error)
app.use(routes.badrequest)
And then inside the /routes/index.js I had:
exports.notfound = function(req, res) {
res.status(404).format({
html: function() {
res.render('404')
},
json: function() {
res.send({
message: 'We did not find what you were looking for :(',
})
},
xml: function() {
res.write('<error>\n')
res.write(
' <message>We did not find what you were looking for :(</message>\n'
)
res.end('</error>\n')
},
text: function() {
res.send('We did not find what you were looking for :(\n')
},
})
}
Now when I call for 404 elsewhere in the app (not in app.js) using res.send(404) I get the right 404 code response but I don't get to the part where it selects whether it shows html or json or text.
How do I do that?
You need to handle error catching differently, here is one way to do so:
Create a middleware after all of your routes that will catch errors you pass to it, the callback would take in an extra parameter containing details about the error:
app.use((err, req, res, next) => {
// Handle the error here
});
Whenever you want to render an error, you can use next in your routes to pass it to this middleware, and pass extra information you can use to decide how to handle the error. There is a module called http-errors that can create objects like that for you. Here is an example route:
const createError = require('http-errors');
app.get('/posts', (req, res, next) => {
// Logic...
if(/* some condition */) {
next(createError(404));
}
});
This will pass the error object created to your error handling middleware, and from there you can choose how to handle it.
To extend this, and to make it work better with asynchronous code, you can wrap your router's callbacks with a function that will make sure exceptions that get thrown are passed over to the error handling middleware, this comes in handy when working with async and await:
// Wrapper function to forward errors from async scopes
const wrap = fn => (...args) => fn(...args).catch(args[2]);
app.get('/posts', wrap(async (req, res) => {
// Logic...
await controller.get('posts'); // Promise rejections will get forwarded to middleware
}));
This also lets you just throw the error object instead of calling next.
Forgive me for these heretical speeches, but I consider express to be the best library for api building from the developer experience point of view. But what stops me from using it everywhere is that everyone keeps saying (and confirming with benchmarks) that it is slow.
I try to choose an alternative for myself, but I canэt find what suits me.
For example with express I can simply organize the following structure:
userAuthMiddleware.js
export const userAuthMiddleware = (req, res, next) => {
console.log('user auth');
next();
};
adminAuthMiddleware.js
export const adminAuthMiddleware = (req, res, next) => {
console.log('admin auth');
next();
};
setUserRoutes.js
export const setUserRoutes = (router) => {
router.get('/news', (req, res) => res.send(['news1', 'news2']));
router.get('/news/:id', (req, res) => res.send(`news${req.params.id}`));
};
setAdminRoutes.js
export const setAdminRoutes = (router) => {
router.post('/news', (req, res) => res.send('created'));
router.put('/news/:id', (req, res) => res.send('uodated'));
};
userApi.js
imports...
const userApi = express.Router();
userApi.use(userAuthMiddleware);
// add handlers for '/movies', '/currency-rates', '/whatever'
setUserRoutes(userApi);
export default userApi;
server.js
imports...
const app = express();
app.use(bodyparser); // an example of middleware which will handle all requests at all. too lazy to come up with a custom
app.use('/user', userApi);
app.use('/admin', adminApi);
app.listen(3333, () => {
console.info(`Express server listening...`);
});
Now it is very easy for me to add handlers to different "zones", and these handlers will pass through the necessary middlewares. (For example users and admin authorization goes on fundamentally different logic). But this middlewares I add in one place and don't think about it anymore, it just works.
And here I am trying to organize a similar flexible routing structure on fastify. So far I haven't succeeded. Either the documentation is stingy, or I'm not attentive enough.
Fastify middlewares that added via 'use' gets req and res objects from the http library and not from the fastify library. Accordingly, it is not very convenient to use them - to pull something out of the body it will be a whole story.
Please give an example of routing in fastify a little more detailed than in the official documentation. For example similar to my example with user and admin on express.
I organize my routes like this:
fastify.register(
function(api, opts, done) {
api.addHook('preHandler', async (req, res) => {
//do something on api routes
if (res.sent) return //stop on error (like user authentication)
})
api.get('/hi', async () => {
return { hello: 'world' }
})
// only for authenticated users with role.
api.register(async role => {
role.addHook('preHandler', async (req, res) => {
// check role for all role routes
if (res.sent) return //stop on error
})
role.get('/my_profile', async () => {
return { hello: 'world' }
})
})
done()
},
{
prefix: '/api'
}
)
Now all request to api/* will be handled by fastify.
I'm running a standard NodeJs 8 with Express and currently when a request for an existing path but un-supported method comes in, Express return 404.
For example 'POST /login' is supported, but 'GET /login' is not, but it returns 404.
How can I make Express return 405 in such a case?
Here's the routes file:
const express = require('express');
const router = express.Router();
const loginController = require('../controllers/login');
router.route('/login').post(loginController.loginUser);
module.exports = router;
Please advise.
You can simply add the .all() handler to your route chain, like so:
const methodNotAllowed = (req, res, next) => res.status(405).send();
router
.route(`/login`)
.post(loginController.loginUser)
.all(methodNotAllowed);
Explanation
This works because requests are passed to the handlers in the order they are attached to the route (the request "waterfall"). The .post() handler will catch your POST requests, and the rest will fall through to the .all() handler.
Also see this question for more details.
Authenticating all POST routes
If you would like to ensure that the user is logged in for all POST requests, but return a 405 response for any other requests, you can use a regular expression to match all routes with router.post('*'), like so:
router
.post(`*`, loginController.loginUser)
.all(methodNotAllowed);
The problem with this approach, however, is that no 404 errors will ever be returned to the client, only 405. Therefore I recommend attaching the methodNotAllowed handler to each individual route, like in the first code snippet above. This approach will return 404 errors for routes that don't exist, but 405 errors for routes that do.
Determining the available methods for a route
To determine which methods are allowed for a route, use router.stack:
app.use((req, res, next) => {
const methods = router.stack
// Filter for the route that matches the currently matched route
.filter(layer => layer.route.path === req.path)[0]
.route
.methods;
if (!methods[req.method]) methodNotAllowed(req, res, next);
else next();
});
You can try this that way:
app.route("/login")
.get((req, res) => {
/* HANDLE GET */
})
.post((req, res) => {
/* HANDLE POST */
})
.all((req, res) => {
res.status(405).send();
});
How it works?
If request matches the route. It will go through the handlers. If a handler is present, it will be handled using that specific one. Otherwise, it will reach the 'all' handler that will set the status code to 405 and send the response.
Here You can find the discussion about it:
405 issue
#You question below:
You can try that way:
loginRoutes.js content:
const router = require('express').Router();
router.route('/')
.get((req, res) => {
res.status(200).send()
})
module.exports = router
server file content:
const express = require('express')
const app = express();
const router = express.Router();
const loginRoutes = require('./loginRoutes')
const PORT = process.env.PORT || 8080;
router.use('/login', loginRoutes)
router.route('/login').all((req, res) => { res.status(405).send() })
app.use(router);
app.listen(PORT, () => console.log(`started on port: ${PORT}`))
You can use this snippet of code to automatically send 405 status code when route from the same path exist but not with the current method
app.use(function (req, res, next) {
const AllLayers = app._router.stack
const Layers = AllLayers.filter(x => x.name === 'bound dispatch' && x.regexp.test(req.path))
const Methods = [];
Layers.forEach(layer => {
for (let method in layer.route.methods) {
if (layer.route.methods[method] === true) {
Methods.push(method.toUpperCase());
}
}
})
if (Layers.length !== 0 && !Methods.includes(req.method)) {
res.setHeader('Allow', Methods.join(','))
if (req.method === "OPTIONS") {
return res.send(Methods.join(', '))
}
else {
return res.sendStatus(405);
}
}
else {
next();
}
});
Hope this could be helpfull to someone
If you want to determine what methods COULD have been used you need to do a lot of digging in the app function you start your server with, and through some string manipulation and the like you can figure out what the possible methods are and return them in the error. If you're interested in how its done check out https://github.com/Justinlkirk/express-ez-405 or just use the npm package here https://www.npmjs.com/package/express-ez-405
Does anyone know if it's possible to get the path used to trigger the route?
For example, let's say I have this:
app.get('/user/:id', function(req, res) {});
With the following simple middleware being used
function(req, res, next) {
req.?
});
I'd want to be able to get /user/:id within the middleware, this is not req.url.
What you want is req.route.path.
For example:
app.get('/user/:id?', function(req, res){
console.log(req.route);
});
// outputs something like
{ path: '/user/:id?',
method: 'get',
callbacks: [ [Function] ],
keys: [ { name: 'id', optional: true } ],
regexp: /^\/user(?:\/([^\/]+?))?\/?$/i,
params: [ id: '12' ] }
http://expressjs.com/api.html#req.route
EDIT:
As explained in the comments, getting req.route in a middleware is difficult/hacky. The router middleware is the one that populates the req.route object, and it probably is in a lower level than the middleware you're developing.
This way, getting req.route is only possible if you hook into the router middleware to parse the req for you before it's executed by Express itself.
FWIW, two other options:
// this will only be called *after* the request has been handled
app.use(function(req, res, next) {
res.on('finish', function() {
console.log('R', req.route);
});
next();
});
// use the middleware on specific requests only
var middleware = function(req, res, next) {
console.log('R', req.route);
next();
};
app.get('/user/:id?', middleware, function(req, res) { ... });
I know this is a little late, but for later Express/Node setups req.originalUrl works just fine!
Hope this helps
This nasty trick using prototype override will help
"use strict"
var Route = require("express").Route;
module.exports = function () {
let defaultImplementation = Route.prototype.dispatch;
Route.prototype.dispatch = function handle(req, res, next) {
someMethod(req, res); //req.route is available here
defaultImplementation.call(this, req, res, next);
};
};
req.route.path will work to get the path for the given route. But if you want the complete path including the path of the parent route, use something like
let full_path = req.baseUrl+req.route.path;
Hope it helps
You can take a look at Router().stack, which has all the routes defined. In your middleware you need to manually compare the available routes with the one called to define actions.
Does anyone know if it's possible to get the path used to trigger the route?
For example, let's say I have this:
app.get('/user/:id', function(req, res) {});
With the following simple middleware being used
function(req, res, next) {
req.?
});
I'd want to be able to get /user/:id within the middleware, this is not req.url.
What you want is req.route.path.
For example:
app.get('/user/:id?', function(req, res){
console.log(req.route);
});
// outputs something like
{ path: '/user/:id?',
method: 'get',
callbacks: [ [Function] ],
keys: [ { name: 'id', optional: true } ],
regexp: /^\/user(?:\/([^\/]+?))?\/?$/i,
params: [ id: '12' ] }
http://expressjs.com/api.html#req.route
EDIT:
As explained in the comments, getting req.route in a middleware is difficult/hacky. The router middleware is the one that populates the req.route object, and it probably is in a lower level than the middleware you're developing.
This way, getting req.route is only possible if you hook into the router middleware to parse the req for you before it's executed by Express itself.
FWIW, two other options:
// this will only be called *after* the request has been handled
app.use(function(req, res, next) {
res.on('finish', function() {
console.log('R', req.route);
});
next();
});
// use the middleware on specific requests only
var middleware = function(req, res, next) {
console.log('R', req.route);
next();
};
app.get('/user/:id?', middleware, function(req, res) { ... });
I know this is a little late, but for later Express/Node setups req.originalUrl works just fine!
Hope this helps
This nasty trick using prototype override will help
"use strict"
var Route = require("express").Route;
module.exports = function () {
let defaultImplementation = Route.prototype.dispatch;
Route.prototype.dispatch = function handle(req, res, next) {
someMethod(req, res); //req.route is available here
defaultImplementation.call(this, req, res, next);
};
};
req.route.path will work to get the path for the given route. But if you want the complete path including the path of the parent route, use something like
let full_path = req.baseUrl+req.route.path;
Hope it helps
You can take a look at Router().stack, which has all the routes defined. In your middleware you need to manually compare the available routes with the one called to define actions.