How to modify static file content with express.static? - node.js

I am using express.static to serve some static files. I want to modify some files content before return to the client. Below is the source code for static resources under /public path.
app.use('/public', express.static(path.join(__dirname, 'public')))
There are some html files under the public directory and I want to modify the html files before responsing to the client.
How can I do that with static? I know I can add customized middlewares but not sure how. Is there a middleware pattern I can use?

You don't use express.static() for files you want to modify - that's not what it does.
Instead, you create routes for the specific URLs you want to do modification on, then you read the file from disk, make your modification and send it back to the client. There are dozens of template systems for the Express eco-system (such as EJS, Jade, Handlebars, etc...) that are specifically designed to solve this problem. Or, if you really want to write your own, you can just load the file yourself, make whatever modifications you want and then send it.
app.get("/public/somefile", function(req, res) {
fs.readFile(path.join(__dirname, 'public', 'somefile'), function(err, data) {
if (err) {
res.sendStatus(404);
} else {
// modify the data here, then send it
res.send(data);
}
});
});
// put express.static after your other routes that serve from the public
// directory so those other routes are matched first
app.use('/public', express.static(path.join(__dirname, 'public')));

Related

Express JS serve public folder via GET endpoint

I have a folder called my-folder, and inside it, many other folders. I'm trying to serve HTML and assets like JS and CSS when I go to a route using Express JS. I'm trying:
router.get('/serve/test', (req, res) => {
app.use(express.static(path.join(__dirname, 'my-folder/test/public')))
})
Inside of my-folder is various folders, and in each of those is again different folders, and I'd like to be able to serve the folder's public directory from any folder within my-folder in order to link to JS, HTML and CSS when going to: http://example.com/serve/test
Right now, I've hard-coded a folder of test, but this would be changed via a param...
Any thoughts as to why this doesn't work?
If you know the parent folders beforehand. You could use multiple express.static outside router.get. Like this:
app.use(express.static(path.join(__dirname, 'my-folder/test/public')))
app.use(express.static(path.join(__dirname, 'my-folder/test2/public')))
...
app.get(...)
If you don't know the folder names beforehand then you will need to build it as a route and return files with res.sendFile:
app.get('/my-folder/:uid/*', function(req, res){
var uid = req.params.uid, path = req.params[0] ? req.params[0] : 'index.html';
res.sendFile(path, {root: `./my-folder/${uid}/public/`});
});
The example above will serve the requested path file or "index.html" if no path is provided.

Express App Serving Unwanted Static Files

I have a react app that is served by express. The entire app is included in public/index.html.
Right now my server.js looks like this:
1 const express = require('express');
2 const path = require('path');
3 const port = process.env.PORT || 8080;
4 const app = express();
5
6 // the __dirname is the current directory from where the script is running
7 app.use(express.static(__dirname));
8
9 app.get('*', (req, res) => {
10 res.sendFile(path.resolve(__dirname, 'public/index.html'));
11 });
12
13 app.listen(port);
However, somehow, files like package.json and /.ssh/known_hosts are being served, things that obviously shouldn't be avaialible.
I'm not sure why app.get('*', (req, res)... isn't catching all requests, and why app.use(express.static(__dirname)); seems to be the only configuration that allows my app to server ANY static files.
I haven't had any luck with app.use(express.static(__dirname+'/public')); or
app.use( '/', express.static(__dirname + '/public'));
or anything else I can find.
EDIT---
My project directory looks like:
/myproject
package.json
server.js
/public
index.html
I want ALL requests to simply serve index.html
What I'm still not understanding is why
app.use('*', express.static(path.resolve(__dirname,'/public/index.html')));
does not serve anything.
Also, why in the above example, res.sendFile() does nothing without first having called express.static(). If I delete line 7, then nothing is served.
So, never ever do this:
app.use(express.static(__dirname));
in your main server directory. That exposes ALL your server files to be viewed.
When using express.static() to serve a whole directory, you should create a directory that contains ONLY files intended for public consumption and then point express.static() at that.
I'm not sure why app.get('*', (req, res)... isn't catching all requests
Because that app.get('*', ...) is AFTER your express.static() so if the express.static() finds a matching file, the app.get('*', ...) never even sees the request. It's already handled and routing doesn't continue any more.
As always with express.static() to advise on exactly what you should do, we need to know the precise configuration you have. Where are the public files in your file system relative to your main server directory and what URLs are you intending to use in the client to refer to those publicly available files.
Here's a theoretical example (since you haven't provided your specifics):
Let's suppose you have files like this:
/myproject
app.js
/public
main.css
And, suppose you want to be able to use /main.css as the URL from the client. For that, you would just do this from within app.js:
app.use(express.static(path.join(__dirname, "public")));
In this first example, where you're serving these static files at the top level path, then you have to make sure there are no naming conflicts between these top level resources and any actual routes you want to serve.
If you wanted the client-side URLs to be /assets/main.css, then you would do this:
app.use("/assets", express.static(path.join(__dirname, "public")));
In this example, you must make sure that the /public sub-directory (and any sub-directories it might have) contains only files intended to be publicly accessible.
Adding a path to the public URL such as /assets removes the chance of a naming conflict between your static assets and your top level routes. This can be a good thing because in any multi-person project, it's not uncommon that the person working on the static assets (like CSS files) is different than the person working on server routes so the two main not be directly aware of what names each other is using. In a single person project, you would certainly just keep it all in your head and avoid accidental naming conflicts that way.
FYI, my preference for folder organization is more like this:
/myproject
/server
app.js
/public
main.css
Where it's 100% obvious which are server files and which are public files. Then, to serve the public files from within app.js with a URL of /assets/main.css, I'd do this:
app.use("/assets", express.static(path.join(__dirname, "../public")));
From your comments:
I just want to serve public/index.html for all ('*') GET requests. All other data comes from separate apis. it seems that res.sendFile() doesn't work without first using express.static. The above code resides in server.js
If all you want to do is to serve public/index.html for all requests (where public is a sub-directory below where app.js is located), then remove the express.static() middleware entirely.
Just use this from app.js:
app.get('*', (req, res) => {
res.sendFile(path.join(__dirname, 'public/index.html'));
});
remove app.use(express.static(__dirname));
as that tells express to load all files that exist
app.use(express.static(__dirname)); will make your app insecure.
Always print out the URLs in the console like path.resolve(__dirname, 'public/index.html') to find out if there is something wrong in it. Seems aiming to the root directory for static files makes problem. Put them in the conventional public directory then try it out again

Change views directory per route in Express?

I want to set the views directory for each route individually, rather than using /views/ for all of them.
So I have a main folder for each view, which has the route and the view in it:
var main = require('./main/main.js');
var test = require('./test/test.js');
app.use('/', main);
app.use('/test', test);
How do I tell that route to look in /main and /test for the view files, rather than in /views for all of them?
I want to be able to just pass the file name in res.render(), rather than a relative path.
Adding multiple views with app.set('views', ['main','test']); is okay, but I'd rather have the views linked to just that route, in case there are files with the same name in different folders. There's also the problem that it expects the layout file to be in the same folder, when I want them all to use the same layout file.
Use Express middleware to change view directory based on the router, You can write a middle either in application level or router level based on your application needs.
Below is the example for application level middleware to change view directory based on route,
app.use(function (req, res, next) {
if (req.path === '/main') app.set('views', './views2');
else app.set('views', './views');
next()
})
Code snippet will check for the request path, If it is /main route it will use views2 directory otherwise it will use views directory.

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.

res.sendfile() doesn't serve javascripts well

I want to use static file serve without any rendering engine.
I've tried to use:
res.sendfile('public/index.html');
on the '/' GET route, and express middleware for static files on my route:
app.use(express.static(path.join(__dirname, 'public')));
BUT it seems like all of the javascripts which the client asks for are downloaded with index.html file information.
How can I make a successful download of the CSS/JS static files ?
UPDATE:
Here is the route for the "res.sendfile ..." :
app.get('/*', index);
I want all of the requests to the server on any route will get index.html and all of its JS&CSS assosiciated with.
I guess this might help you...
in app.js file...
app.use(app.router);
app.use(express.static(path.join(__dirname, 'public')));
app.use("/styles", express.static(__dirname + '/public/stylesheets'));
app.use("/scripts", express.static(__dirname + '/public/javascripts'));
app.use("/images", express.static(__dirname + '/public/images'));
// development only
if ('development' == app.get('env')) {
app.use(express.errorHandler());
}
app.get('/', function (req, res) {
res.sendfile(__dirname + '/public/home.html');
});
save the home.html inside the /public folder and JavaScript files in /public/javascripts, images in /public/images, css files in /public/stylesheets folder.
In the HTML file reference should be the words you define(eg: /scripts/home.js)... like this
<link rel="stylesheet" type="text/css" href="/styles/home.css" >
<script src="/scripts/home.js" type="text/javascript"></script>
var express=require("express");
var app=express();
app.use('/', express.static(__dirname + '/website/views/'));
app.set("views",__dirname+'/website/views');
app.get("/",function(req,res){
res.sendfile('index.html');
});
the codes above is mine.i wish to help you.
Why not something like this?
if(req.url == '/') { // Root lookups appear to be mapped to index.html
next();
} else {
fname = [disk location of your website] + req.url;
fs.open(fname, 'r', function(err, fd) {
if(err) {
next();
} else {
fs.close(fd);
res.sendfile(fname);
}
});
}
Well, the simplest solution will be to move app.use(express.static(path.join(__dirname, 'public')))
up before you call app.use(app.router);
This way, the static middleware gets served before the app.get('/*', index);
I've made an assumption here that your routes are declared in this order:
app.use(app.router);
app.use(express.static(path.join(__dirname, 'public')));
If this is indeed the case, then the following suggestion holds:
The problem here is the app.get('/*', …) will intercept all requests that match, which is basically everything. Your static middleware won't get a chance at serving files.
If you remove that route, things should work for you as index.html is already in the public directory and can be served by the static middleware.
For a good explanation of how this works, see the answer to this question: node.js / express.js - How does app.router work?
Update based on additions to the above question:
You've stated this as the current behavior of your server:
it seems like all of the javascripts which the client asks for are downloaded with index.html file information.
You have asked the question:
How can I make a successful download of the CSS/JS static files ?
with this requirement
I want all of the requests to the server on any route will get index.html and all of its JS&CSS assosiciated with.
Your question and requirement are opposed to each other. The server will send back to the client exactly what you tell/configure it to. It will either always send back index.html which is exactly what your requirement states, or it will successfully serve up both index.html and any CSS/Javascript it references which is what your original problem statement was.
In one of your comments below you've stated:
the reason I want to do it, is because I'm using templates, and index.html wraps each template. I'm using angular on the client, and I'm starting to realize that i'll have to use a render engine in order to achieve this. Again, my angular client defines the partial url, and when it sends the request to : '/partial/sample' I need the index.html to wrap the 'sample.html' for instance
My assumptions based on this statement (please correct if wrong)
You are using client side templates
The files you are retrieving from the server are static (i.e., they need to be served up as is from the server)
Your routes are currently declared in this order
app.use(app.router);
app.use(express.static(path.join(__dirname, 'public')));
You are not doing any server side templating (i.e. everything is located under public somewhere)
If these assumptions are correct, the fix is to do what I originally suggested and remove this route:
app.get('/*', index);
If you do this (assuming your resources are referenced correctly):
your index.html will be retrieved as is from the server via the static middleware.
Each css/js file you've referenced in index.html will be returned from the server via the static middleware
Any requests to load template files (such as sample.html) will be serviced by your static middeware and returned to the client without modification

Resources