I'm rewording this since I have figured out what the problem is, but I cant understand how its happening or how to resolve it.
Basically, in all of my routes I have to places where I am adding a DELETE route. The first is as follows
app.route('/bi/clubs/:id')
.post(authentication,clubController.getAll)
.delete(authentication,clubController.deleteClub);
The second location is
app.route('/bi/clubs/members')
.post(authentication,memController.getAll)
.delete(authentication,memController.deleteMember);
Whenever I call DELETE on the '/bi/clubs/members', express is actually routing that to '/bi/clubs/:id'. I have actually gone through and traced that this is occurring.
I can validate that I am not adding the route anywhere else in the app, and if I comment out the DELETE route on '/bi/clubs/id', then the second DELETE route will route properly. If I don't comment it out, the call to DELETE '/bi/clubs/members' will route to '/bi/clubs/:id'.
The order in which I am setting the routes is just as above.
Any help would be much appreciated.
Express is matching your route /bi/clubs/membersas /bi/clubs/:id. When a request is made to an Express app your app will start at the top of your routes and middleware and work its way down to the end hitting all the routes that it matches.
When you tell Express to match the route /bi/clubs/:id, all you are telling it is match bi, then clubs and then a dynamic value that you are referring to as id. Although you are probably looking for id to be a number or a MongoDB ID, Express doesn't know the difference, so technically the string members matches as a dynamic value. Just not the one you want.
If you console.log the value of id it should be members. Your static routes need to be registered before your dynamic routes.
If you have any questions or would like an example please let me know.
Related
Oddly, when I moved my middleware in front of my routes, my routes stopped working. I got no error, on the client or the server.
My fetch() calls seemingly disappeared.
However when I put the routes before my middleware, the routes begin to work again.
I'm refactoring code and have no idea why this is happening.
// load middleware
const middleware = require(__dirname +'/middleware');
app.use(middleware.session.session);
...
// load routes
const routes = require(__dirname + '/routes');
app.use('/articles', routes.articles);
...
Routes and middleware should be placed in the order that you want them to be matched against the route and then to execute (if they match the incoming route). There is no absolute answer to which comes first as it really depends upon what you're trying to do. For example, you would put app.use(express.json()) before any route that wants to use a JSON formatted body. But, you would put the login page route handler BEFORE any middleware that checks to see if the user is authenticated (so that it can run for users that aren't yet authenticated).
If your code stops working when you put the middleware first, then there is probably something wrong with the implementation of the middleware. We can only help with that specific issue if you show us the actual code for that middleware. Perhaps you aren't calling next() to continue routing or you're sending a response in the middleware or there's a coding error in the middleware.
I am working on an express application and I have two GET URLs. One fetches all resources in the database but is right-protected(needs authentication and admin access) while the other fetches resources based on a search parameter(query string).
The route that requires authentication looks like so:
carRouter.get('/car', verifyToken, isAdmin, fetchAllCarAds);
This means that the admin has to be logged in first and then a check is carried out to ascertain whether he is truly an admin before he is given access. Now I have another GET route that doesn't require authentication like so
:
carRouter.get('/car?status=unsold', filterUnsoldCars);.
I understand that express does not allow routing based on query strings so how do I ensure that the request that does not require authentication(query string) is accessible in the one without query string?
You can do the following things to make it work.
Check either query string exist or not inside isAdmin middleware
If query string exists, Skip validation that has been implemented inside middleware.
If query string doesn't exist, then check either user is an admin or not.
I fixed it by placing it before the verifyToken function and it worked. I would like to learn of other ways of doing it if there are. Thanks
carRouter.get('/car', filterUnsoldCars, verifyToken, isAdminDummy, fetchAllCarAds);
I dont think you are building a proper routing but, you can use a middleware to check query params and bypasses to the next route assigned. check below routing explanation for next('route')
Express Routing
carRouter.get('/car', hasParams, verifyToken, isAdminDummy, fetchAllCarAds);
carRouter.get('/car?status=unsold', filterUnsoldCars);
if hasParams true, next('route')
if hasParams false, next()
I'm working with an express application. There are some express routes, as
server.get('*' , ... )
etc. which perform some common operations: authentication, validation... etc.
they also decorates the response with meaningful information: i.e. in every request to the server it gives not only the expected json/html, but also information regarding the user, some app metadata that the front-end consumes etc. etc.
Let's say all this extra metadata cames in a field called extradata in every request to the server.
Now, there is a bug that is causing a problem: instead of returning its expected response (a json with a bunch of system logs), is sending only this extradata field.
I'm pretty confident the problem is in one of the middlewares, because that code that sends the response in this case is really simple, it's just a res.send() of a json. So I believe this part of the app is requiring some module that sets a middleware which causes the error. There are a lot of global vars and implicit parameters in the app so is really difficult to debug it manualluy.
I attempted to bypass such middlewares programmatically, like:
delete server._router.stack[2];
but is causing an TypeError: Cannot read property 'route' of undefined and thus preventing my app to build: sure this is not the way to go.
so, is there a way to programmatically ignore or bypass express routes that are yet set?
Even better, is there a way to programmatically tap into express middlewares and log every request and response?
(afaik, there are libreries like morgan that logs every request, but I don't think they apply to this case since I need to discriminate between middlewares).
What I generally do is simply use the next method. You can access it by simply passing it to the callback function. Something like:
app.use(function(req, res, next) {
if(...) {
next();
} else {
...
}
}
What this is going to do is go to the next middleware.
So if I understand correctly, you can check what you exactly need in the if-statement and do things accordingly.
What I would suggest is you read the Express API documentation, especially the section about middleware, which you can find here. Moreover, try to isolate the suspects and solve the issue by removing the problem, rather than deleting handlers and trying to solve the problem the easy way.
app.use("*", topUsers) is called multiple times..
topUsers is being called multiple times
function topUsers(req, res, next){
console.log("req.url", req.url)
findMostUsefullReviewsAggregate(6)
.then(function(aggregationResult){
// console.log("aggregationResult", aggregationResult)
// console.log("***aggregationResult::", aggregationResult)
return populateMostUseful(aggregationResult)
})
.then(function(populated){
console.log("<<<<<<<<<<<<<<<<<<TOPUSER CALLED >>>>>>>>>>>>")
// console.log("POPULATED: ", populated);
console.log(">>>populateMostUseful.length");
populated = populated.map(function(e){
e.momented = moment(e.createdAt.getTime()).fromNow();
return e;
})
req.session.mostUsefulReviews = populated;
// res.end()
})
.catch(function(err){
console.log("HEADERERR", err);
});
next();
}
( some info for later: main.ejs is for ("/"))
when I change it to app.get("/", topUsers) and go to "/"it is only called once (which is what I want).
the console.log for req.url shows "/" for all 3
In my main.ejs I include a header.ejs. I thought that might be a problem. Maybe the request to the header for the include was a problem but I don't think so.
Quick question : If I do app.get("/") would that work on all subroutes like /users/idofuser? I think If I do that the function doesn't get called.
I think app.get("*") also gives me the same problem with the multiple calls.
Edit: when I put next in the .then() it still gets called multiple times.
my real goal it to have something happen on all routes. I already have routes set up. I don't want to go to each route and do something like app.get("/", onemiddleware, topUsers, ). I don't want to put topUSers on each one physically. Do I have to.
If you are doing this from a browser loading a web page, the browser is probably requesting multiple resources from the website.
For starters, it often requests the favicon and if there are any other resources in the web page (other scripts, stylesheets, images, etc...) each of those will be a separate request to your web page. You can see exactly what URL is being requested by logging req.url for each request. From a single page load, there should be no reason you would get three requests for /, but you very well could get a request for / follow by other requests for some other resource (and consequently some other URL) from the same server.
Show us the web page you are loading and we can better point out what's going on.
If what you're really trying to do is to share a middleware on a set of routes, then you can create a router, put the middleware on the router with router.use(), define all the routes that you want to have that middleware on that specific router and then hook the router into your app. This will execute the middleware only for routes that match that router and you only have to specify the middleware once for the router and all routes defined on that router will use that middleware. Other routes defined directly on the app object or on other routers will not use the middleware. You will have to define the router as some unique path root or wildcard that helps express know which routes should be passed into your router. We'd have to see the whole universe of paths you plan to use that both do and don't use that middleware for us to know more specifically what to suggest.
Like this question, I want to dynamically add ui-router states, but I'm not sure how to do it given the following:
We start with a small set of routes (max 5) that allow the user to navigate the login process: The user logs in (multi step login process), then selects the product type and role they want to use (assuming user has more than one product type/role, else they will bypass this view). At that point, I want to go out to my service and get the list of routes the user has access to, given the userid, product type, & role - I plan to send down an array of data structures that very closely match what is provided to $stateProvider.state(...).
In my app.run.js, I am checking $rootScope.$on("$stateChangeStart" and moving the user along from view to view in the multi-step login process and therefore know when I need to go out to my service to load up the available routes.
When the routes are loaded, I'll probably put a flag in a cookie to indicate that it was properly loaded for this user/product/role.
I don't think a provider really makes sense here since I don't want the routes loaded at config. It feels wrong (not sure why) to call the service to load the routes in the stateChangeStart.
Is this approach a reasonable one?
Note: I also see that there is an outstanding request to be able to remove states from $stateProvider. In the meantime until this request is implemented, how do we clear the $stateProvider of routes (apart from a browser refresh)?
For adding states after the config phase, you should use the Future State functionality in the ui-router-extra package.
Since there's no official support for deleting routes, you could probably merge in this PR to get something going, but you'd have to add functionality to remove the condition from urlRouterProvider as well since that's a bug with that PR.