In express, I have defined some routes
app.post("/api/v1/client", Client.create);
app.get("/api/v1/client", Client.get);
...
I have defined how to handle requests inside a Client controller. Is there a way that I can do some pre-processing to the requests, before handling them in my controller? I specifically want to check if the API caller is authorized to access the route, using the notion of access levels. Any advice would be appreciated.
You can do what you need in a couple of ways.
This will place a middleware that will be used before hitting the router. Make sure the router is added with app.use() after. Middleware order is important.
app.use(function(req, res, next) {
// Put some preprocessing here.
next();
});
app.use(app.router);
You can also use a route middleware.
var someFunction = function(req, res, next) {
// Put the preprocessing here.
next();
};
app.post("/api/v1/client", someFunction, Client.create);
This will do a preprocessing step for that route.
Note: Make sure your app.use() invokes are before your route definitions. Defining a route automatically adds app.router to the middleware chain, which may put it ahead of the user defined middleware.
Related
I'm wondering if it's possible to essentially "reevaluate" the middleware conditions without actually changing the current route.
The middleware's purpose is to prevent non-logged-in users from accessing the "dashboard".
My issue is, a user could become logged in or logged out without necessarily changing route but they wouldn't be redirected until they try and change pages.
I have a VueX action that triggers when the user's auth state changes but this (from what I can see), can't access the redirect or route variables.
// /mixins/auth.js
const reevaluateAuthStatus = (store, redirect, route) => {
console.log(route)
const redirectPolicy = route.meta.map((meta) => {
if (meta.auth && typeof meta.auth.redirectPolicy !== 'undefined') { return meta.auth.redirectPolicy[0] }
return []
})
const user = store.getters['auth/getUser']
if (redirectPolicy.includes('LOGGEDOUT')) {
if (user) {
return redirect('/dashboard')
}
} else if (redirectPolicy.includes('LOGGEDIN')) {
if (!user) {
return redirect('/login')
}
}
}
module.exports = {
reevaluateAuthStatus
}
// /middleware/auth.js
import { reevaluateAuthStatus } from '../mixins/auth'
export default function ({ store, redirect, route }) {
reevaluateAuthStatus(store, redirect, route)
}
Appreciate any help on this :)
You cannot re-evaluate a middleware AFAIK because it's mainly this (as stated in the documentation)
middlewares will be called [...] on the client-side when navigating to further routes
2 clean ways you can still achieve this IMO:
use some websockets, either with socket.io or something similar like Apollo Subscriptions, to have your UI taking into account the new changes
export your middleware logic to some kind of call, that you could trigger again by calling the $fetch hook again or any other data-related fetching hook in Nuxt
Some more ugly solutions would probably be:
making an internal setInterval and check if the actual state is still valid every 5s or so
move to the same page you are actually on with something like this.$router.go(0) as somehow explained in the Vue router documentation
Still, most of the cases I don't think that this one may be a big issue if the user is logged out, because he will just be redirected once he tries something.
As if the user becomes logged-in, I'm not even sure on which case this one can happen if he is not doing something pro-active on your SPA.
I don't know if it's relevant or not, but I solved a similar problem this way:
I have a global middleware to check the auth status. It's a function that receives Context as a parameter.
I have a plugin that injects itself into context (e.g. $middleware).
The middleware function is imported here.
In this plugin I define a method that calls this middleware passing the context (since the Plugin has Context as parameter as well): ctx.$middleware.triggerMiddleware = () => middleware(ctx);
Now the middleware triggers on every route change as intended, but I can also call this.$middleware.triggerMiddleware() everywhere I want.
Using express, I have multiple routes where I need some middleware. They aren't under any common /url/path.
Router-level middleware says:
Router-level middleware works in the same way as application-level middleware, except it is bound to an instance of express.Router()
As I read it, the implication is that the middleware applies only to the routes of the router instance. But that seems not to be the case.
In the following example, I expect "router called" to be logged only for http://localhost:10000/router.
But it gets logged for http://localhost:10000/noRouter also, which I don't understand. Why does middleware used by the router instance get called for routes that are added to app directly? Is it possible to create a Router so that only routes bound to that Router get the middleware applied?
const express = require('express')
const app = express()
const port = 10000
const router = express.Router();
router.use((req, _res, next) => {
console.log("router called")
next()
})
app.use(router)
router.get('/router', (req, res) => {
res.send("ok")
})
app.get('/noRouter', (req, res) => {
res.send("ok")
})
app.listen(port, () => {
console.log(`Listening at http://localhost:${port}`)
})
P.S.: I'm not stuck and have it working using a different approach. I just want to understand why this doesn't work...
A router is just inserted into a chain of request handlers and is searched/executed in order.
Because you do this:
app.use(router)
You are specifically sending ALL requests to your router. So, all requests will wind through the various handlers in that router looking for one that matches the incoming path. That will include your router.use() middleware which matches ALL paths so it will execute for every URL that gets sent to your router. There is no logic in a router that first checks to see if some route in the router matches before it executes any middleware. A router works just like the app object in this regard and executes middleware in the order it encounters them. So, the only way your middleware doesn't get executed is if the request never gets to the router at all.
If you want middleware to only apply to routes in a router, then you have a couple choices:
Put the router on a common path prefix such as:
app.use("/somerouterprefix", router);
Then, only URLs that get routed to your router will run the middleware in the router. This will, of course, only execute the middleware for routes that start with that prefix, but it will also execute the middleware for routes that start with the prefix, but don't even have a matching route handler in the router. Remember, I said earlier that everything that gets sent to the router will cause your middleware to execute.
Or, secondly, put the middleware on each individual handler in your router so that it will only get executed when it matches some route on your router such as:
const router = express.Router();
// give the middleware a function name so you can
// use it in specific route definitions
function myMiddleware((req, _res, next) => {
console.log("router called")
next()
});
// specify the middleware in your route definition
router.get('/router', myMiddleware, (req, res) => {
res.send("ok")
});
I have this express.js code:
app.get('/', (req, res) => {
res.render('homePage', { title: 'Express' });
});
app.use((req, res) => {
console.log('use() is executed');
res.end();
});
If the request is handled by the get() method, use() is not supposed to execute. But I noticed that it still execute even though everything was fine and the page is rendered. Did I do something wrong or did I miss something? Thanks.
From Express Doc:
Link: http://expressjs.com/en/4x/api.html#app.use
app.use([path,] callback [, callback...])
Mounts the specified middleware function or functions at the specified path: the middleware function is executed when the base of the requested path matches the path.
app.use() register the function as middleware, and you didn't specify the path. That's why it will run every time when any route is called.
The placement of the middlewares will change the execution order. However, they don't change the fact that the middleware will be executed as, well middlewares, which will be executed before the actual functions with in the routes.
I am attempting to make an AXIOS request within router.beforeEach. However, it looks like the request is being made with my next destination URL being prepended; if trying to access /client/create, the beforeEach appears to prepend '/client/create' to the request.
Instead of '/api/participant/test/{some_id}' the request is being sent to '/client/create/api/participant/{some_id}'.
I'm not quite sure why this is happening. The documentation indicates that you could use a getPost() method to make requests:
beforeRouteEnter (to, from, next) {
getPost(to.params.id, (err, post) => {
next(vm => vm.setData(err, post))
})
},
However it seems the getPost() method is unrecognized, which could be because of the beforeEach call (the documentation does not show that this could be used with this particular method).
Here is the code with the AXIOS request.
router.beforeEach((to, from, next) => {
console.log(to.params.id);
// Check to see if the cookie exists
if (document.cookie.match(/^(.*;)?\s*participant_token\s*=\s*[^;]+(.*)?$/)) {
axios.get('api/participant/test/' + to.params.id)
.then(function (response) {
console.log(response.data);
})
.catch(function (error) {
console.log(error);
});
}
Any ideas on how this can be avoided? I imagine one could use beforeRouteEnter for each of the routes I have set up, but this would be a more elegant solution.
It should be
axios.get('/api/participant/test/' + to.params.id)
(with forward slash character at the beginner).
The more properly way to set baseURL in axios config
For example:
axios.defaults.baseURL = 'your base url';
First, there is no built-in getPost method in Vue.js. Documentation has mentioned it just as an illustration purpose.
Also, use root relative URL instead of relative URL that you are trying to use.
axios.get('/api/participant/test/' + to.params.id)
You are trying to use relative URL which is causing a problem for you. The more generic way would be to set default base URL in Axios global config.
All:
I am new to Express 4 router.
When I tried some login/signup example, I got one question about the .use and .get/.post function:
I saw sometimes it uses:
var express = require('express');
var router = express.Router();
router.get('/hello', function(req, res, next) {
res.send("Welcome");
});
and in main app, we use it like:
app.use("/", router);
While some other time, it uses:
var express = require('express');
var router = express.Router();
//here the router uses .use() function rather than .get/.post
router.use('/hello', function(req, res, next) {
res.send("Welcome");
});
and in main app, we use it like:
app.use("/", router);
So I am wondering what is the difference between them, does the .use() just a general name for all of get/post/put/... together?
I find this post: Difference between app.use and app.get in express.js
But still not feel easy to understand this....
Thanks
In addition to what Jonathan Lonowski said in the posted link, it might help to not compare use to get and post, but to compare it to all because both all and use work regardless of the HTTP verb used while that's obviously not true for get. Everything I'm about to say applies if you replace "all" with "get", it'll just narrow that handler down to a specific HTTP verb.
So, what's the difference between all and use?
app.all will handle incoming requests at the specified URL path regardless of the HTTP verb, just as app.use does. However, how it compares the requested URL to the handler is different. For example:
var r = express.Router();
r.use('/foo', function (...) { ... }); // Route A
r.all('/bar', function (...) { ... }); // Route B
If you make a request to /foo/123 Route A will be run.
If you make a request, however, to /bar/123 Route B will NOT be run.
This is because with HTTP verbs express compares the full path, but with 'use' it only cares about the beginning of the url. Because the URL /foo/123 begins with /foo Route A will run, but because /bar/123 does not match the FULL URL, Route B will not be. Note: You could make .all behave in the same way: r.all('/bar/*', ...), but use is easier and more appropriate for this.
So, what you would tend to mount with one vs the other is different. For example:
var app = express();
var router1 = express.Router();
var router2 = express.Router();
router2.all('*', function (req, res) { ... }); // Must specify a path!
router1.use('/secondary-routes', router2); // Can't do this with all.
app.use(router1); // Look Ma, no path!
Here I've used all to handle a request coming in, where I've used use to mount an entire router. Also, note that the usage of router.METHOD functions require a URL string as the first parameter, while use does not.
At the end of the day, if you:
Want all requests that come in under a given path (or even every request) to use the specified middleware, or
Want to mount an entire sub router/application, or
Want to include a plugin into your application
... Then use is probably what you want.
If you:
Are handling a specific request at a specific URL path (i.e. probably not doing a * match in the URL)
Generally won't be calling next and will instead actually be handling the request
... Then an HTTP verb method (like get, post or all) is probably what you want.
.use is used in 2 cases, middlewares and "modular mountable route handlers".
In your example
router.use('/hello', function(req, res, next) {
res.send("Welcome");
});
This means that any requests sent to /hello will be terminated with "Welcome" and the actual .get attached to /hello will not be called.
So, in short, call use when you need to apply some general middlewares or want to do modular architecture with routers. use can be "used" as request handlers, but you shouldn't because it is not designed for that purpose