I would like to intercept a req.params item in an interception layer:
let's say I have an express server application instance:
const app = express();
with an interception layer:
app.use((req, res, next) => {
console.log(req.params.myParam);
next();
})
and many endpoints like this one:
app.get('/anything/:myParam', (req, res) => res.send('hello'))
this logs 'undefined' which is quite natural because when the interception middleware within the "use" is executed, the param name has not been defined yet. But I really need to known in this interception layer the value of myParam parameter with these constraints:
I known how the parameter is supposed to be named in the intercepted endpoints (myParam)
I can't know how the url is structured in the intercepted endpoints (can be /anything/:myParam, or /:myParam/anything and so on...)
Does anyone know a solution for that case ?
The comment from #indrajaj26 about using app.param() leads me on a working solution.
Here is the way I'm proceeding:
/** express application */
const app = express();
// Interception layer
app.param('myParam', (req, res, next, myParam) => {
/* Store myParam into something reusable (like locals as suggested by #nullromo for instance).
* I'm using zonejs in my case: creating a forked zone with myParam stored in zone properties.
*/
next();
});
// Consuming intercepted data. Anywhere in the callstack of any endpoint using "myParam" as request parameter:
console.log(Zone.current.getZoneWith('myParam')?.get('myParam'));
Thanks everyone for your ideas!
A simple solution would be to pass the middleware function when defining the route.
Example:
function middleware(req, res, next) {
console.log(req.params.myParam);
next();
}
app.get('/path', middleware, function(req, res) {
...
});
Full example:
const express = require('express');
const app = express();
function middleware(req, res, next) {
console.log(req.params.myParam);
next();
}
app.get('/path', middleware, function(req, res) {
...
});
app.listen(3000, () => console.log('server is online'));
You can store the params on the response.locals object for use by later middleware.
const express = require('express');
const app = express();
app.get('/:name', (request, response, next) => {
// generate some kind of result
const result = `Hello, ${request.params.name ?? 'world'}!`;
// save the result and the params onto the request.locals object
response.locals = { params: request.params, result };
// call the next middleware to pass on the params and result
next();
});
app.use((request, response, next) => {
// here you have the params since they were parsed by the previous middleware
console.log(`Intercepted: ${JSON.stringify(response.locals.params)}`);
// now actually send the response that you wanted to send
response.status(200).send(response.locals.result);
});
app.listen(3000, () => {
console.log('listening');
});
This requires storing the request.params on the response.locals object in every handler, which is not the greatest. But it also means that you don't have to specify the interceptor middleware every time, if that's a problem for your case.
Related
so i have a very basic blog app made in express, and the thing is that when i respond to app.get('/') and render a home page view, the server is still responding to app.get('/:id') that is further in the code.
I didn't call next() in app.get('/'), and from what i know, when i render a view, then the rest of middleware after shouldn't get executed, so why is it executing app.get('/:id')?
const express = require('express');
const mongoose = require('mongoose');
const app = express();
// Setting up basic middleware
app.use(express.static('public'));
app.use(express.urlencoded({ extended: true }));
app.set('view engine', 'ejs');
app.set('views');
// Database things
// ...
// Home page
app.get('/', (req, res) => {
res.render('index')
})
// Get add new blog page
app.get('/add-new', (req, res) => {
res.render('addblog')
})
// Respond with single blog page
app.get('/:id', (req, res) => {
const id = req.params.id;
Blog.findById(id)
.then(data => res.render('blog', { data }))
})
// 404
app.use('/', (req, res) => {
res.render('404')
})
I did not understand properly what you meant to ask but from what I understood, I think you mean to ask that even if someone calls /123, (Here 123 is the id) the code to be executed should be
app.get('/', (req, res) => {
res.render('index')
})
If this is what you mean to ask, then that's not how it's done. Only www.yoursite.com/ request goes to app.get('/', (req, res) and any route having value after www.yoursite.com/ like www.yoursite.com/123 will go to the other route which renders single page. 1 more thing. As you have written that if you didn't call next, rest of the middleware shouldn't execute after render, that is not the case. The entire middleware has to execute unless there is an error or you call the return statement like return res.render or simply return.
your 404 Route should be :
app.get('*', function(req, res){
res.render('404');
});
every middelware you have to return something in you case:
return res.render('the-page')
without return it will not work
Background:
I have an express application with some simple routes and Router-level Middleware. I want to register an Application-Level Middleware.
The Problem
In Router-Level Middlewares. I can access req.route object. But I can not access the same object inside an Application-Level Middleware.
I can understand this, as inside Application-Level middlewares the program is not inside the route yet.
But is there any way to get req.route object or something equivalent to req.route.path inside global middlewares?
req.path or req.originalUrl contains the real url not the route path.
Example
const express = require('express');
const app = express();
const port = 3232;
app.use((req, res, next) => {
const route = req.route; // It is an application level middleware, route is null
return next();
});
app.get('/test/:someParams', (req, res) => {
const route = req.route; // can access req.route here because it is a Router-level middleware
console.log(route.path)
console.log(req.path)
return res.send('test')
});
app.listen(port, () => console.log(`Example app listening on port ${port}!`));
Output
Request: GET#localhost:3232/test/33333
/test/33333 // I don't need this.
/test/:someParams // This is what I want to get inside the Application-Level Middleware
Alternative Solution
An alternative solution to this problem can be as the following
const express = require('express');
const app = express();
const port = 3232;
function globalMiddleware(req, res, next) {
const route = req.route;
console.log(route) // can access it
return next();
}
app.use((req, res, next) => {
const route = req.route; // It is an application level middleware, route is null
return next();
});
app.get('/test/:someParams', globalMiddleware, (req, res) => {
const route = req.route; // can access req.route here because it is a Router-level middleware
});
app.listen(port, () => console.log(`Example app listening on port ${port}!`));
But injecting the same middleware to each and all of my routes does not sound like a smart solution. Specially on bigger applications.
A dump of router object
{
"path":"/test/:someParams",
"stack":[
{
"name":"globalMiddleware",
"keys":[
],
"regexp":{
"fast_star":false,
"fast_slash":false
},
"method":"get"
},
{
"name":"<anonymous>",
"keys":[
],
"regexp":{
"fast_star":false,
"fast_slash":false
},
"method":"get"
}
],
"methods":{
"get":true
}
}
the path key is the thing I want to get. Please note that req.route.path is not the same as req.path
I was having this difficulty too. I didn't find anything on the internet that would solve it so I tried to search the express code itself for what it did to find the route. Basically it is the same thing as the code below, it looks for which regexp is valid for that route. (Google Translate)
const url = req.originalUrl.split('?')[0] // Routes with query
const layer = req.app._router.stack.find(layer => {
return layer.regexp.exec(url) && layer.route
})
So you can access the original path:
console.log(layer.route.path)
What data do you expect in req.route?
you can use req.url, req.method, req.originalUrl, etc...
or in Application level middleware you can add new field to req object
req.customRoute = {yourField: "yourValue"}
and this field will available in route level middleware
You can use middleware as per request
const middleware = (req, res, next) => {
// Append what you want in req variable
req.route = "abc" // let abc
req.path = "path" // req.route.path
next();
}
You can get it here from middleware
app.get('/test/:someParams', middleware, (req, res) => {
console.log(req.params.someParams)
console.log(req.route) // "abc"
console.log(req.path) // "path"
});
For application level middleware
app.use((req, res, next) => {
req.route = "abc" // let abc
req.path = "path" // or req.route.path
next()
})
The way I have this app set up, every request gets serviced by the same route that will do some JS serverside rendering:
server.use("*", (req, res) => {
console.log(`from the server route: ${req.path}`)
const context = {};
const serverRenderedHTML = ReactDOMServer.renderToString(
React.createElement(
StaticRouter,
{ location: req.url, context },
React.createElement(AdminApp)
)
);
if (context.url) {
res.redirect(context.url);
}
res.write(template({ serverRenderedHTML }));
res.end();
});
But, I would also like to use my express server as an api for some resources, routes set up like this:
//server.js
server.use("/api/products", productRoutes);
// products.js
router.get( (req, res) => {
var productQuery = Product.find({})
productQuery.exec(function(err, products){
res.json(products)
})
})
However, I am unable to hit the API, as all of my requests are getting picked up by that server.use function.
Is there any way to make sure that the routes under /api/ namespace are properly picked up? Should I just make a different server for the API?
Apparently, I needed to include an argument for the route. I had to change:
router.get( (req, res) => ...
to
router.get("/", (req, res) => ...
If I have a simple express router that just returns nothing (undefined), is there a way to recover from it or will the request just hang until there's a timeout?
For example if I had this code:
const express = require('express');
const app = express();
const router = express.Router();
router.use((req, res, next) => {
console.log('stuck');
return;
});
app.use(router);
app.get('/', (req, res) => res.send('Hello World!'));
app.listen(3000, () => console.log('Example app listening on port 3000!'));
The app hangs after printing stuck. If I return next() the request continues, but I'm trying to see if there's a way to recover from an empty return statement without calling next() at some point.
Thanks in advance!
Since you don't want to call next(), you could probably wrap the handler.
Something like this:
const withReturnNext = (func) => (req, res, next) => {
func(req, res);
next();
}
app.use(withReturnNext((req, res) => res.set('x-headername', 'value'));
However, this is not advisable because any async code will return before the asyncronous action is finished, leading to next() being called before it was supposed to.
You have to use next() to continue the middleware chain. If you return from the function you just terminate the chain and if subsequent function is responsible for the response you won't get it. If you want to terminate (for example because something went wrong) but still finish the request you can use something like that:
app.use(function (err, req, res, next) {
console.error(err.stack)
res.status(500).send('Something went wrong')
})
I am using current express 4 and node.js 6.x. In my web application I need to do an internal redirect (i.e. redirect from one route handler to another, without sending a redirect to the client). This works within toplevel express or within the same router, but not if the source request is located in a Router.
Please see the following minimal working example:
const express = require("express");
const app = module.exports.app = express();
const router = new express.Router();
app.use("/myrouter", router);
// not working
router.get("/test", (req, res, next) => {
req.url = "/toplevel";
next();
});
// working
app.get("/test", (req, res, next) => {
req.url = "/toplevel";
next();
});
// debug output
app.use((req, res, next) => {
console.log(`request to ${req.url}`);
next();
});
app.get("/toplevel", (req, res) => {
res.end("toplevel");
});
app.listen(8000);
If I open "http://localhost:8000/test", I get the correct output "toplevel", and the debug output prints "request to /toplevel". Unfortunately it doesn't work for "http://localhost:8000/myrouter/test". The debug output prints "request to /myrouter/toplevel" and I get the result of the default express 404 handler.
Is there any may to redirect the request from "/myrouter/test" to "/toplevel" without redirecting the client (i.e. res.redirect())?