Reputation: 8834
I have a node app sitting like a firewall/dispatcher in front of other micro services and it uses a middleware chain like below:
...
app.use app_lookup
app.use timestamp_validator
app.use request_body
app.use checksum_validator
app.use rateLimiter
app.use whitelist
app.use proxy
...
However for a particular GET route I want to skip all of them except rateLimiter and proxy. Is their a way to set a filter like a Rails before_filter using :except/:only?
Upvotes: 50
Views: 75745
Reputation: 21
I wanted to make sure that if I hit all other methods except POST /user/login, then I should get 404 - Not Found And same for POST /user/signup
router.post("/signup", userSignup).all("*", (req, res) => res.sendStatus(404));
router.post("/login", userLogin).all("*", (req, res) => res.sendStatus(404));
router.get("/:id", getParticularUser);
router.patch("/:id", editUserDetails);
Above solved my problem, just posting if this is regarding what I wanted to know.
Upvotes: 0
Reputation: 5772
Improved upon @Geelie's answer with added types:
import {Request, Response, NextFunction, Handler} from "express";
const unless = (middleware: Handler, ...paths: RegExp[]): Handler => {
return function (req: Request, res: Response, next: NextFunction) {
const pathCheck = paths.some(path => path.test(req.path));
pathCheck ? next() : middleware(req, res, next);
};
};
app.use(unless(redirectPage, new RegExp("/user/login"), new RegExp("/user/register")));
Upvotes: 0
Reputation: 11
In my case I used part of answers posted yet to override original app.use
const unless = ( route, middleware ) => {
return ( req, res, next ) => {
if ( req.originalUrl.startsWith( route + '/' ) ) {
return next();
} else {
return middleware( req, res, next );
}
};
};
const filteredRoute = '/myapi'; // Route to filter and subroute
const origUse = app.use;
app.use = function ( ...callbacks ) {
if ( !callbacks.length ) throw new Error( '.use() method requires at least one function' );
if ( typeof callbacks[0] ==='string' ) {
if ( !( callbacks.length -1 ) ) throw new Error( '.use() method requires at least one function' );
const route = callbacks.shift();
for ( let i = 0; i < callbacks.length; i++ ) {
origUse.call( this, route, unless( filteredRoute, callbacks[i] ) );
}
} else {
for ( let i = 0; i < callbacks.length; i++ ) {
origUse.call( this, unless( filteredRoute, callbacks[i] ) );
}
}
};
Upvotes: 1
Reputation: 2631
Even though there is no build-in middleware filter system in expressjs, you can achieve this in at least two ways.
First method is to mount all middlewares that you want to skip to a regular expression path than includes a negative lookup:
// Skip all middleware except rateLimiter and proxy when route is /example_route
app.use(/\/((?!example_route).)*/, app_lookup);
app.use(/\/((?!example_route).)*/, timestamp_validator);
app.use(/\/((?!example_route).)*/, request_body);
app.use(/\/((?!example_route).)*/, checksum_validator);
app.use(rateLimiter);
app.use(/\/((?!example_route).)*/, whitelist);
app.use(proxy);
Second method, probably more readable and cleaner one, is to wrap your middleware with a small helper function:
var unless = function(path, middleware) {
return function(req, res, next) {
if (path === req.path) {
return next();
} else {
return middleware(req, res, next);
}
};
};
app.use(unless('/example_route', app_lookup));
app.use(unless('/example_route', timestamp_validator));
app.use(unless('/example_route', request_body));
app.use(unless('/example_route', checksum_validator));
app.use(rateLimiter);
app.use(unless('/example_route', whitelist));
app.use(proxy);
If you need more powerfull route matching than simple path === req.path
you can use path-to-regexp module that is used internally by Express.
UPDATE :- In express 4.17
req.path
returns only '/', so use req.baseUrl
:
var unless = function(path, middleware) {
return function(req, res, next) {
if (path === req.baseUrl) {
return next();
} else {
return middleware(req, res, next);
}
};
};
Upvotes: 116
Reputation: 10040
There's a lot of good answers here. I needed a slightly different answer though.
I wanted to be able to exclude middleware from all HTTP PUT requests. So I created a more general version of the unless
function that allows a predicate to be passed in:
function unless(pred, middleware) {
return (req, res, next) => {
if (pred(req)) {
next(); // Skip this middleware.
}
else {
middleware(req, res, next); // Allow this middleware.
}
}
}
Example usage:
app.use(unless(req => req.method === "PUT", bodyParser.json()));
Upvotes: 5
Reputation: 368
The way I achieved this is by setting up a middleware for a specific path like so
app.use("/routeNeedingAllMiddleware", middleware1);
app.use("/routeNeedingAllMiddleware", middleware2);
app.use("/routeNeedingAllMiddleware", middleware3);
app.use("/routeNeedingAllMiddleware", middleware4);
and then setting up my routes like so
app.post("/routeNeedingAllMiddleware/route1", route1Handler);
app.post("/routeNeedingAllMiddleware/route2", route2Handler);
For the other special route that doesn't need all the middleware, we setup another route like so
app.use("/routeNeedingSomeMiddleware", middleware2);
app.use("/routeNeedingSomeMiddleware", middleware4);
and then setting up the corresponding route like so
app.post("/routeNeedingSomeMiddleware/specialRoute", specialRouteHandler);
The Express documentation for this is available here
Upvotes: 0
Reputation: 7063
Here's an example of using path-to-regexp
as @lukaszfiszer's answer suggests:
import { RequestHandler } from 'express';
import pathToRegexp from 'path-to-regexp';
const unless = (
paths: pathToRegexp.Path,
middleware: RequestHandler
): RequestHandler => {
const regex = pathToRegexp(paths);
return (req, res, next) =>
regex.exec(req.url) ? next() : middleware(req, res, next);
};
export default unless;
Upvotes: 0
Reputation: 571
Built upon the answer from @lukaszfiszer as I wanted more than one route excluded. You can add as many as you want here.
var unless = function(middleware, ...paths) {
return function(req, res, next) {
const pathCheck = paths.some(path => path === req.path);
pathCheck ? next() : middleware(req, res, next);
};
};
app.use(unless(redirectPage, "/user/login", "/user/register"));
Can't add as comment sorry.
Upvotes: 57
Reputation: 163
You can define some routes like below.
app.use(/\/((?!route1|route2).)*/, (req, res, next) => {
//A personal middleware
//code
next();//Will call the app.get(), app.post() or other
});
Upvotes: 2
Reputation: 129
I use this regular expression with success : /^\/(?!path1|pathn).*$/
.
Upvotes: 5
Reputation: 6254
You can also skip route like this by putting a condition on req.originalUrl:
app.use(function (req, res, next) {
if (req.originalUrl === '/api/login') {
return next();
} else {
//DO SOMETHING
}
Upvotes: 24