Express - Setting different maxAge for certain files - node.js

I'm building a single page application using Express for the backend and AngularJS for the frontend and I'm having some cache problems.
I'm not using express views, only serving files with express.static middleware.
My static files are in public/app or public/dist depending on enviroment (dist has minified files).
app.use app.router
app.use express.static(cwd + '/public/' + publicFolder, maxAge: MAX_AGE)
When requesting '/', app.router verifies if user is logged in, and if everything is ok then index.html is served via express.static (I just call next() in the controller). If user is not logged in, it gets redirected to login.html
My problem is that if I set maxAge, my index.html file gets cached and the first request to '/' doesn't go through router. I can enter the app even if I'm not logged in.
If I set maxAge to 0, the problem goes away, but I want to cache all my *.js and *.css files.
What is the correct approach to this kind of problems? Start using views? Different express.static mount points?

You can always define individual routes without necessarily using views (although view templates aren't a bad idea). In this way you could define index.html just to apply the special case maxAge.
Just be sure to put the route before the static middleware.
If you wanted you could even use send, the same static server that the static middleware uses behind the scenes. Something like:
// install send from npm
var send = require("send");
app.get("/index.html", function (req, res) {
send(req, "/index.html")
.maxage(0)
.root(__dirname + "/public")
.pipe(res);
});
Or the more low level stream way, something like:
app.get("/index.html", function (req, res) {
res.setHeader('Content-Type', 'text/html');
res.setHeader('Cache-Control', 'public, max-age=0');
// Note that you'd probably want to stat the file for `content-length`
// as well. This is just an example.
var stream = fs.createReadStream(__dirname + '/public/index.html');
stream.pipe(res);
});

Related

NodeJS Express — Serve generated index.html public file without saving it

When using express, the expectation is that you'll serve a public directory.
const app = express();
app.use('/', express.static('./public/'));
Is there a way I could serve a generated file instead? For my application, it would be much more convenient if I could build the index.html directly, then serve that 'file' directly from memory, without having to save it just to then serve it via 'use'.
the expectation is that you'll serve a public directory
I don't think that is the expectation at all. Many applications just use routes instead making a REST micro service.
There are two ways you can do what you want to do.
Use a templating engine with NodeJS and just res.render() the template. Check this out for more information, even though the article is using .pug you can use these ones as well. Popular ones are ejs, handlebars
app.get('/', function (req, res) {
res.render('index', { title: 'Hey', message: 'Hello there!' })
})
Or you can write everything inside res.send() for example:
app.get('/', function (req, res) {
//set the appropriate HTTP header
res.setHeader('Content-Type', 'text/html');
//send multiple responses to the client
res.send('<h1>This is the response</h1>');
});

better way to verify jwt token for angular app deployed on nodejs

I have created a server using the express framework. I have implemented the JWT. I have created an angular app then copied to the public folder. Then used
app.use(express.static(__dirname + "/public"));
to make this public. Now I want to authenticate my angular routes. Now there could be a call for the URL that will be URL of angular routing like localhost:7000/a, localhost:7000/b and so on. Additionally, there will be calls for the actual files like CSS, images or other files.
So I have used a middleware to intercept all the server calls.
app.use((req, res, next) => {
let pwt = req.cookies ? req.cookies.pwt : null;
jwt.verify(pwt, process.env.SECRET_KEY, (err, decoded) => {
if (decoded) {
res.sendFile(path.join(__dirname, 'public', 'index.html'));
} else {
let callbackUrl = encodeURIComponent("http://" + req.headers.host + req.originalUrl);
res.redirect(process.env.AUTH_SERVER + "/auth?_r=" + callbackUrl);
}
});
});
I am checking the jwt token and return the index.html file for each request. when there is call for image or any other CSS files it returns the index.html file.
One way is to make an array of Angular routing URLs and serve the index.html file only if req.originalUrl is from that list. But this is not the good approach, as the server should not have the knowledge of app routing. Is there any way to solve this problem?
I think the best way to do this is to implement routes in angular and secure it there if you are building SPA, but with express middleware just secure your API request from your angular app.

How to use static folder but also use routing in Express?

I'm having a major issue with Routing in Express.
In my project, there is a folder /public.
Inside /folder I have some other folders like
- public
|- user
|- common
Initially, the only way pages were served by my Node.js server was through res.sendFile(../..). The problem was that the .js files and .css files did not know where to go.
So I added
app.use('/', express.static(__dirname + '/public'));
But now the problem is, if I try to visit /user what happens is, that the static version of the index.html file in that folder is returned, and my route defined in the node app is ignored!
This route - and most importantly the auth middleware is never touched!
How do I reconcile the need to serve files from a static folder, but also may have routes of the same name that I want to control more closely?
app.use('/user', userauth, userRoutes);
Assuming /user is some sort of API I don't think express.static was intended to be used that way. I think the best solution would be to use different routes. You could add something like /api/ to the beginning of all your routes or make sure your static files are organized under /css, /js, /partials, etc.
If you absolutely must support /user for static files AND your api calls you will need to ditch express.static and use something like the Accept header with custom middleware that determines what the browser is asking for. Depending on the scenario this may get complicated to support all the variables. A simple example would be something like this:
app.use(function(req, res, next){
// check Accept header to make sure its a JSON request
if(req.accepts('application/json') && !req.accepts('html')) {
// JSON request so forward request on to the next middleware(eventually hitting the route)
return next();
}
// NOT a JSON request so lets serve up the file if it exists...
// TODO use req.path to check if file exists - if not 404 - will also need to map paths
// to /users to check for /users/index.html if you need to support that
var fileExists = false;
return fileExists ? res.sendFile('...') || res.status(404).send('Not Found');
});
You would have to make sure when the client calls /users it sets the Accept header to application/json. Most client side frameworks have a way to do this for all AJAX requests.

Express not catching request to '/'

I have a quite bizzare issue - somehow, Express does not capture my request to my root route.
My Route File looks to following:
'use strict';
var errors = require('./components/errors');
var auth = require('./controllers/auth');
var ensureLoggedIn = require('connect-ensure-login').ensureLoggedIn;
module.exports = function(app) {
// Insert routes below
// All undefined asset or api routes should return a 404
app.route('/:url(api|auth|components|app|bower_components|assets)/*')
.get(errors[404]);
app.route('/login')
.get(auth.login)
.post(auth.loginUser);
app.route('/logout')
.get(auth.logout);
// All other routes should redirect to the index.html
app.route('/*')
.get(ensureLoggedIn('/login'), function(req, res) {
console.log("req to /");
res.sendfile(app.get('appPath') + '/index.html');
});
};
So what happens:
I request '/' and it sends me directly to my root and the app runs. Except: It does not require me to login, and also the Log Output does not show that any request has been made.
If i request '/users' (Angular Route) it redirects me to '/login', as expected and then continues on its path.
Any idea what would be causing this behavior?
Are you exposing any static assets with express? If you are and you require your routes after you expose the assets, the request to "/" will just land in your public folder and do nothing.
Does this help? Could you post your server.js?
I ran into this issue earlier and it was driving me nuts.
It seems like it's related to the filename of the 'main' Page being served, before Angular takes over.
Originally, the file was names index.html which, as it seems like, prompted Node to serve it by default - Which is expected behavior, if no routes interfere.
After renaming the main file to application.html and changing the serve asset - all is well, problem solved.
I am still unsure though, why the Route would not fire and the index would overwrite it.

How to use AngularJS routes with Express (Node.js) when a new page is requested?

I'm using Express, which loads AngularJS from a static directory. Normally, I will request http://localhost/, in which Express serves me my index.html and all of the correct Angular files, etc. In my Angular app, I have these routes setup, which replace the content in an ng-view:
$routeProvider.when('/', {
templateUrl: '/partials/main.html',
controller: MainCtrl,
});
$routeProvider.when('/project/:projectId', {
templateUrl: '/partials/project.html',
controller: ProjectCtrl,
});
$locationProvider.html5Mode(true);
On my main page, I have a link to <a href="/project/{{project.id}}">, which will successfully load the template and direct me to http://localhost/project/3 or whatever ID I have specified. The problem is when I try to direct my browser to http://localhost/project/3 or refresh the page, the request is going to the Express/Node server, which returns Cannot GET /project/3.
How do I setup my Express routes to accommodate for this? I'm guessing it will require the use of $location in Angular (although I'd prefer to avoid the ugly ?searches and #hashes they use), but I'm clueless about how to go about setting up the Express routes to handle this.
Thanks.
with express 4, you probably want to catch all requests and redirect to angularjs index.html page.
app.use(app.router); doesn't exist anymore and res.sendfile is deprecated, use res.sendFilewith an uppercase F.
app.post('/projects/', projectController.createProject);
app.get('/projects/:id', projectController.getProject);
app.get('*', function (req, res) {
res.sendFile('/public/index.html');
});
put all your API routes before the route for every path app.get('*', function (req, res){...})
I would create a catch-all handler that runs after your regular routes that sends the necessary data.
app = express();
// your normal configuration like `app.use(express.bodyParser());` here
// ...
app.use(app.router);
app.use(function(req, res) {
// Use res.sendfile, as it streams instead of reading the file into memory.
res.sendfile(__dirname + '/public/index.html');
});
app.router is the middleware that runs all of your Express routes (like app.get and app.post); normally, Express puts this at the very end of the middleware chain automatically, but you can also add it to the chain explicitly, like we did here.
Then, if the URL isn't handled by app.router, the last middleware will send the Angular HTML view down to the client. This will happen for any URL that isn't handled by the other middleware, so your Angular app will have to handle invalid routes correctly.
I guess I should have clarified that I wasn't interested in using a template engine, but having Angular pull all of the HTML partials on it's own, Node is functioning completely as a static server here (but it won't be for the JSON API. Brian Ford shows how to do it using Jade here: http://briantford.com/blog/angular-express.html
My app is a single-page app, so I created an Express route for each possible URL pattern, and each of them does the same thing.
fs.readFile(__dirname + '/public/index.html', 'utf8', function(err, content) {
res.send(content);
});
I was assuming I would have to pass some request variables to Angular, but it looks like Angular takes care of it automatically.

Resources