First of all, i have searched the solution to this problem and i didn't found anything. Sorry if it's duplicated.
I have in my express+node.js app two endpoints like this:
// Gets a tweet by unique id
app.get('/tweets:id', function(req, res, next) {
// Response management
});
// Gets mentions of user unique id
app.get('/tweets/mentions', function(req, res, next) {
// Response management
});
The problem is that requesting a GET petition to "/tweets/mentions", is attended first by "/tweets/:id" and later by "/tweets/mentions", making a conflict.
I have tried to change the declaration order of the endpoints, but always the request is attended by both endpoints.
Also I have tried things like "/tweets::mentions", but I need to access the endpoint via "/tweets/mentions", and I suppose there is a possible way.
How can i resolve this conflict?
Thanks.
Are you using next() in one of the handlers?
next() passes control to the next matching route, so in your example, if one of them is called and inside it you call next(), the other one will be called.
I allways recommend to use 'Router' if you have more than one base path because it helps you to keep it organized.
You can resolve the conflict by checking the value of req.params.id in the "tweet by id" handler.
For routes with additional parameters is always recommended to not use the same base path of other routes.
Something like could work for you:
app.get('/tweets/users/:id', function(req, res, next) {
// Response management
});
// Gets mentions of user unique id
app.get('/tweets/mentions', function(req, res, next) {
// Response management
});
Related
I wish to know how can I have both get and post request handled by app.use the way I do it using app.route
app.use('/', (req, res, next) => {
if (isLaunched) {
return next()
}
// You can also render if you want
res.render('coming-soon')
});
How can I handle a post request to this?
According to https://expressjs.com/en/guide/using-middleware.html the syntax you already have is used for any type of HTTP request - including GET and POST. You can detect the method via req.method.
app.use() already handles ALL http methods, including GET and POST. You can see exactly which method it is for any given request by checking req.method.
If you had trouble with some GET or POST when doing this, then please show the specific code and the specific request that it didn't work for. If you didn't try it yet, then just try it as it should work just fine.
Middlewares are mounted using app.use(<middleware-name>) so, you can add it to all routes like you do for bodyParser/CORS etc.
If you want to mount for specific routes you can use
app.post("/example" , middleware, (req,res)=>{
res.send("Hello world")
})
Refer to Use middleware on specific routes
I'll start with some context:
I have a RESTful API Server that includes routes for managing users; e.g.
PUT .../users/:id/profile
As part of our authentication flow we verify the user's identity and compare the id in the url with the id retrieved from our IDP.
What I'm trying to do is implement a 'me' replacement approach; where a user, instead of providing their id in the url, can simply provide 'me' instead, and the id is reassigned with id retrieved from the IDP based on their authentication. I've seen Google do this for some of their APIs. E.g.
.../users/me/profile
So far I've been attempting a simple replacement with the assumption that a modified req.params will propagate forward:
req.params.id = req.params.id === 'me'
? session.id
: req.params.id
This wasn't working so I did a bit of reading up on it and apparently req.params gets built with each middleware function (when .use() is called), so reassigning req.params.id doesn't propagate throughout the app as I had hoped.
Currently all our business logic pulls the id from req.params so I was wondering if there's a way to get the above behaviour to work to avoid refactoring all my business logic?
I understand that req.params is built from parsing the url; so would modifying the url be an option?; to replace 'me' with the id; so that req.params is populated as intended? If so, how could this be achieved?
Or, if you have a nicer alternative to getting the above 'me' replacement behaviour to work then I'm all ears!
Global middleware (that you add using app.use() or router.use()) is typically not even aware of any route handlers that declare parameters, so req.params usually isn't even populated.
A solution would be to "inline" the modifying middleware:
app.put('/users/:id/profile', middleware, ...)
But that would require rewriting all your route handlers.
As you already point out correctly, you can modify the URL:
app.use(function(req, res, next) {
if (req.url === '/users/me/profile') {
req.url = '/users/1234/profile';
}
next();
});
app.put('/users/:id/profile', ...)
Instead of having to match each URL specifically, you can perform substring matches, or use a list of all URL's that may contain an :id parameter (and therefore, may contain the me identifier).
I just realized that this should work (using req.param()):
router.param('id', function(req, res, next, id) {
if (id === 'me') {
req.params.id = '1234';
}
next();
});
This should be added to routers that declare parameters, either in route handlers directly, or in mountpoints.
A convoluted example:
let childRouter = express.Router({ mergeParams : true });
childRouter.put('/profile', ...);
let parentRouter = express.Router({ mergeParams : true });
parentRouter.use('/:id', childRouter);
parentRouter.param('id', function(req, res, next, id) {
if (id === 'me') {
req.params.id = '1234';
}
next();
});
app.use('/users', parentRouter);
Because parentRouter declares a mountpoint /:id, it should get the resolving function.
I'm having trouble getting my .get() to work. My understanding of .use() vs .get() is that .use() is used for paths we want to apply things to, while .get() is more specific (choosing specific urls to apply to)
In my example, I want to show 'Applies to all pages' to any page number, but if I type in '/28', I want the .get message to show up instead. Right now, I get the .use message even when I go to /28.
router.use('/:id', function(req, res){
console.log("Things " + Date.now());
res.send('Applies to all pages ' + req.params.id);
});
router.get('/28', function(req, res){
res.send('Secret 28 page');
});
The use method is used to employ all the http verb to certain path from your express app or express router. You have to consider the precedence while using use. Here, what is happening is that you have already employed use in your dynamic router as router.use('/:id', ...) which will take get request as well.
You can employ the get router first, so that it only take getrequest in /28 and transfer all the unhandled request to other path.
router.get('/28', function(req, res){
res.send('Secret 28 page');
});
router.use('/:id', function(req, res){
console.log("Things " + Date.now());
res.send('Applies to all pages ' + req.params.id);
});
From the docs:
The order in which you define middleware with router.use() is very important. They are invoked sequentially, thus the order defines middleware precedence.
In your example your '/:id' will take precedence when matching.
So swap the order to fix your example, and in general, define more specific handlers first.
Try switching the order of your .get and use.
ExpressJS goes in sequential order when executing. Read this post for more clarification:
https://derickbailey.com/2016/05/09/in-what-order-does-my-express-js-middleware-execute/
Also, a little unrelated but you can use the .get middleware first, and pass the next step using a third parameter to your callback which is next(), if you haven't known that already.
.use() applies to all the paths regardless of HTTP verb, so in your case it applies to GET, POST, PUT and all the other requests that start with /:id path and app.get("/28") is such a request
app.use('/api', require('./api'));
app.use('/', require('./cms'));
The first route is for my public api, the second is the cms dashboard. But this will not work, because localhost:80/api will still load the second route too.
How to solve this? I know I can do app.use('/cms', require('./cms')); but the route would be localhost:80/cms/blog which is not fine for me.
I think the function you pass to the app.use has 3 parameters, the third one being next() callback, which calls the next middleware.
If you define your function as below, and don't call next, then it shouldn't trigger the next middleware( route too perhaps ).
app.use('/api', function(req, res, next) {
require('./api');
});
Whatever your require'd file contains, it probably receives the "next" function as its parameter. Dont call it.
update: Also, in your middleware , call res.end() to quit other routes
What's in your ./cms file? It might be that the server isn't closing the response, so it continues on to the next route.
There are many ways to mock requests using things like supertest and nock but what I'm looking to do is to be able to create fake request objects, as if the request was going to a different URL, that can be passed on to other processing functions.
app.get("/render", function(req, res) {
// how do I do this?
var fake = createFakeRequest("/bar/?baz=qux", req);
// I want doStuff to believe the request came to /bar/?baz=qux
doStuff(fake, function(err, result) {
res.send(result);
});
});
I'm aware I could modify all of he variables such as req.path, req.query, req.url but I'm worried I may miss something, and it seems like someone out there must have already solved this problem.
The reason I need this behavior is that for any given URL in my CMS, there can be multiple drafts. Each draft will have different content elements, but those individual content elements may have logic based on the URL or query parameters. So even though the request came in as /render/?draft=1&url=/foo/bar/, I want the content element processors to believe the request came in to /foo/bar/ and be oblivious to the version system which actually handled the initial HTTP request.
Not sure to understand but seems like url rewriting, so using a middleware could work
function urlRewrite(req, res, next){
req.url ="...";
next();
}
and so
app.use(urlRewrite);
Be sure to use it at the right place (depending on your server goal)
Cause we maybe need params before the rewrite... and if rewrite, you may need it after...
EDIT
In my framework:
server.before(extractPath);
server.before(urlParams);
server.before(pathParams);
server.get("/foo", route.foo);
So I could write
server.before(extractPath);
=> server.before( function(req, res, next){
urlRewrite(req, res, function(){
extractPath(req, res, next);
}
});
server.before(urlParams);
server.before(pathParams);
server.get("/foo", route.foo);
If urlRewrite depends on urlParams, I could write
server.before(extractPath);
server.before(urlParams);
=> server.before( function(req, res, next){
urlRewrite(req, res, function(){
extractPath(req, res, function(){
urlParams(req, res, next);
});
});
});
server.before(pathParams);
server.get("/foo", route.foo);
As I said, it depends on your framework