Serve out 'static' route with express with redirect and middleware - node.js

I am serving out a static url with express 4.0:
app.use('/static-route', express.static('./static'));
And that works great.
However I would like to redirect my users to a url with a query parameter if they hit that route.
ie /static-route -> /static-route?someQueryParam=hello
I would also like to include middleware for that static request. As a concrete example I am using passport and would like to make sure the user is logged in to access that static content.

app.use (and app.get etc . . .) doesn't take two parameters, the first parameter is the route (optional for use), then the rest are all middleware.
app.use('/static-route', function (req, res, next) {
// validation
// redirect
// etc . . .
next();
}, express.static('./static'));

Use global wilcard route[ app.use('/') ] for static content and
Use specific routes [ app.get(/myroute), app.post('/anotherroute')] for dynamic processing using custom logic
//Serves resources from public folder
app.use('/',express.static(__dirname + '/public'));
//Verify the complete directory path - especially slashes
console.log('Static directory '+__dirname + '/public');
app.get('/list', function (req, res) {
res.send('<html><body><h1>Hello World</h1></body></html>'); });

Related

Node express api routes for multilingual directory like url

Does any one knows an example or could explain here how node.js and express would have to route for a multilanguage site? I'm using i18n-node for translation and folder like routing ( /es/, /de/ , etc ) for different languages. This all are static routes but I also have routes like apiRoutes.route('/user/profile') using 'app' at the begining ( app.get('/app/user/profile') so please consider this in your answer so is NOT necesary route to : app.get('/es/app/user/profile') .
having 15 routes like this now:
app.get('/terms', function(req, res) {
res.render('terms',{
...
});
});
how it have to be set for routes like:
app.get('/es/terms', function(req, res) {
res.render('terms',{
...
});
});
Should I duplicate this routes and add for example a locale for
each like:
app.get('/es/terms', function(req, res) {
res.render('terms',{
...
});
});
Or Should do something like:
if cookie['lang'] && cookie['lang'] is in locales
// then redirect to /:lang/terms
else
// show default language in /terms
if req.headers["accept-language"] && req.headers["accept-language"]
// then redirect to /:lang/terms
else
//show default language in /terms
Or there is another way I should approach this that follows good practices or is better respecting standards?
Miro's Answer in :
How can I get the browser language in node.js (express.js)? says I should use app.all('*', ...
Is this all I need?, ..still, it might have a syntax error or i'm not understanding well this two parts
var rxLocal = /^\/(de|en)/i;
...
app.get(/\/(de|en)\/login/i, routes.login);
thanks in advance
You need to consider 2 things :
1. How get the local :
Accept-Language
The HTTP protocole define the Accept-Language header to manage the local. This is a normalized method. You can access it with the req.acceptsLanguages method of express.
+Normalized
+Natively support by brower
-Not easy to by passe by the end user
Path / Cookies
You can get the local from the path. In express it can be do with a parameter patter like /:local/rest/of/path and retrieve in the request object with the req.param method.
You can also get the information from the cookies with the req.cookies properties (don't forgot to set it).
Both
To increase the user experience you can mix the both method. For exemple get the default language from the HTTP header send by the browser but permite to the user to override this in you application and store this parameter in the cookies.
2. Use the local:
Each methods to get the local can be used from different way. I will
use random of them in exemple but they are all compatible.
Top level configuration.
In case of you use a template Engine and you controller can be local agnostic. You can use a middleware to get the local information and configure the render engine.
app.use('/:local' (req, res, next) => {
let localKey = req.param('local');
res.locals = // Some ingenious method to get the locales from localKey
next();
}
Check res.locals and your engine documentation.
Use it in controller.
If the local is part of the contoller process. You can get directly is value in controller.
In case of you use a complexe method to determine the final value of the local, you can also use a middleware to determine this value and enrich the request with it.
app.use((req, res, next) => {
let local = req.cookies.local;
if(!local) local = req.acceptsLanguages();
if(!local) local = 'en-US';
req.local = local;
}
Both
You can use both method too. It depend of what you need. Find the best way to get a maintainable code and avoid replication for your use case.
When you use middle where witch impact the controllers, be sure you declare them before your routes.
You can use a route parameter to get the locale from the URL, like this:
app.get('/:lang/terms', function (req, res) {
if (req.params === 'es') {
res.send('¡Hola!');
else {
res.send('Hi!');
}
});
The colon character tells Express to put whatever is between the first to slashes of the path in req.params.lang.
See express routing documentation for details.

Determining path in Express router routes

I've set up an express app and using the routing middleware to abstract some routes into a separate include.
I reference them using this style in the app.js:
app.use('/foo', my_urls);
This means "/foo/bar" in the browser is handled as if it's "/bar".
The problem though is that in the router.get("/bar"...) section in the included router file, I need to know the value of the preceding (foo) part. I've set up the route that this "foo" could be anything from an array of values.
Is there any way to know the context of the routing middleware, the preceding part of the path that the routes are acting within? In other words, can I do something like (pretend code here):
router.get('/bar', function(req, res, next) {
res.send(req.path[0]) // foo
});
Solved: Thanks for the answer. I can get the value using this:
router.get('/bar', function(req, res, next) {
res.send(req.baseUrl.splice(1)) // foo
});
express has req.path property so you can know the path but is shows path after parent Router's path (if you have one), so to get parent router path you can use req.baseUrl and req.originalUrl to get full url with queryparam.

node.js: serve static web, match request url //*/web/ to file system /web/

I use node.js in a simple way to serve a static web.
...
app.use(express.static('./build'));
http.createServer(app).listen(port, ipaddress);
...
This serves the files 1:1 (with index.html as default resource), e.g.
//server/a.html -> ./build/a.html
//server/bbb/x.html -> ./build/bbb/x.html
//server/ccc/ -> ./build/index.html
But now, I need to be able to remove 'one level' of the request url, but it shall serve still the same web, e.g.
//server/aaaa/a.html -> ./build/a.html
//server/bbbb/a.html -> ./build/a.html
//server/xxxx/bbb/x.html -> ./build/bbb/x.html
//server/yyy/ccc/ -> ./build/ccc/index.html
So I need a wildcard matching in the request url. I tried this:
app.use('/\*', express.static('./build'));
http.createServer(app).listen(port, ipaddress);
But with no luck. No more page is accessible. What is wrong?
[Edited to show that the server should serve index.html as default resource]
Depending on your application, you might put express.static() on separate Router instances that are mounted on your app. For example:
var routerA = new express.Router();
// You could also reuse the same static file handler since they
// are all using the same root path
routerA.use(express.static('./build'));
// and other `routerA` route handlers ...
var routerB = new express.Router();
routerB.use(express.static('./build'));
// and other `routerB` route handlers ...
// etc.
However if you don't have your application broken up like this already, you could also specify multiple routes like:
app.use(['aaaa', 'bbbb', 'xxxx'], express.static('./build'));
Or if nothing else, you could just use a custom middleware, calling the static file handler manually (although this is kind of a hack, as it was what separate, mounted Routers were designed to help solve):
var staticHandler = express.static('./build');
app.use(function(req, res, next) {
var m = /^\/[^/]+(\/.+)$/.exec(req.url);
if (m) {
// Temporarily override the `req.url` so that the path
// concatenation will happen correctly
var oldUrl = req.url;
req.url = m[1];
staticHandler(req, res, function(err) {
// Reverting the to the original `req.url` allows
// route handlers to match the request if a file
// was not found
req.url = oldUrl;
next(err);
});
} else
next();
});
app.get('/aaa/foo', function(req, res) {
res.end('hello from /aaa/foo!');
});
My final solution is:
// serve all files from ./web directory regardless of first element in url
app.get('/:leveltoremove/*', function(req, res) {
var path = req.params[0] ? req.params[0] : 'index.html';
res.sendfile(path, {root: './web'});
});
http.createServer(app).listen(port, ipaddress);

404 when accessing new route

I'm trying to add a new route (/profile) to my NodeJS Express web application. I've modified my app.js file like this:
var routes = require('./routes/index');
var profile = require('./routes/profile');
app.use('/', routes);
app.use('/profile', profile);
The '/' index path works fine, my issue is with '/profile'. Whenever I try to access it, I get a 404. This is profile.js:
var express = require('express');
var router = express.Router();
router.get('/profile', function(req, res) {
var username = req.session.username;
if(username) {
res.render('profile');
} else {
res.redirect('/login');
}
});
module.exports = router;
I don't understand what I'm doing wrong because in the example express application that is generated, '/users' works fine. I basically copied that format, but it's throwing a 404. Any ideas?
In my profile.js, I had to change my GET request path to this:
router.get('/', function(req, res) {
//code
});
Otherwise, the router would be looking for /profile/profile. When I change it to /, it's just looking for the root of `/profile', or at least that's how I understand it.
To understand what you are doing wrong you should know that Node.js uses middleware functions to route your requests. To simplify you can think about it as a chain of functions.
Middleware is like a plumbing pipe, requests start at the first middleware you define and work their way “down” the middleware stack processing for each path they match.
So with the following statement you added a middleware function to handle any request starting with the root path /profile, and it is a common pattern in Node to use the use method to define the root paths.
app.use('/profile', profile);
The use method is doing part of the routing in your scenario and the statement above will match any route starting with that path, including /profile/all or /profile/12 or even /profile/go/deeper/inside.
However, you want to narrow down that routing to something more specific, so that is why you pass a router middleware function (profile in your case) to match more specific routes instead of all routes starting with /profile.
The profile middleware function is actually the next step in the chain of functions to execute, and it will start from the root path specified in the use statement, which is the reason why you need to start again with / and not with /profile. If you wanted to match a profile by ID you would do:
router.get('/:id', ...)
Which would be concatenated with the base URL (from the /use statement) and would match a request like /profile/2 or /profile/abc.

NodeJS + Express: How to secure a URL

I am using latest versions of NodeJS and ExpressJS (for MVC).
I usually configure my rest paths like this, for example:
app.get('/archive', routes.archive);
Now i want my /admin/* set of URLs to be secured, I mean I need just simple authentication, it's just a draft.
When a user tries to access, for example, /admin/posts, before sending him the corresponding view and data, I check for a req.session.authenticated. If it's not defined, I redirect to the login page.
Login page has a simple validation form, and a sign-in controller method: if user does send "right user" and "right password" I set the session variable and he's authenticated.
What I find difficult, or I don't understand, is how to actually make the "filter" code, I mean, the auth check, before every /admin/* path call.
Does this have something to do with "middleware" express functions?
Thank you
Yep, middleware is exactly what you want. A middleware function is just a function that works just like any other Express route handler, expept it gets run before your actual route handler. You could, for example, do something like this:
function requireLogin(req, res, next) {
if (req.session.loggedIn) {
next(); // allow the next route to run
} else {
// require the user to log in
res.redirect("/login"); // or render a form, etc.
}
}
// Automatically apply the `requireLogin` middleware to all
// routes starting with `/admin`
app.all("/admin/*", requireLogin, function(req, res, next) {
next(); // if the middleware allowed us to get here,
// just move on to the next route handler
});
app.get("/admin/posts", function(req, res) {
// if we got here, the `app.all` call above has already
// ensured that the user is logged in
});
You could specify requireLogin as a middleware to each of the routes you want to be protected, instead of using the app.all call with /admin/*, but doing it the way I show here ensures that you can't accidentally forget to add it to any page that starts with /admin.
A even simpler approach would be to add the following code in the App.js file.
var auth = function(req, res, next) {
if(isAdmin) {
return next();
} else {
return res.status(400)
}
};
app.use('/admin', auth, apiDecrement);
As you can see the middleware is being attached to the route. Before ExpressJS goes forward, it executes the function that you passed as the second parameter.
With this solution you can make different checks before displaying the site to the end user.
Best.
Like brandon, but you can also go the connect route
app.use('/admin', requireLogin)
app.use(app.router)
app.get('/admin/posts', /* middleware */)

Resources