How to use static folder but also use routing in Express? - node.js

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.

Related

Which headers indicate a request for static assets

Using Express we see middleware like so:
app.use('/', express.static(path.resolve(__dirname + '/public')));
app.use('/', function(req,res,next){
// if we get here, the request was not for static assets
});
my question is - which headers does Express use to check that the request is for static assets (files, etc)?
I want to replicate some middleware that does what Express does for static assets.
A typical HTTP request does not have any special headers that suggest that a client might be requesting a static asset over something else. In fact, there is concept of a 'static asset' in HTTP. HTTP is completely obviously to an asset being static, or not.
Anything can be 'static' or 'dynamic'. The middleware probably works because it will check the filesystem if a file exists.

What is the best way to conditionally serve static files in Express?

I'm building an application that has two separate parts to it, which on the frontend I am building as two separate Angular apps. I am doing it this way so that I can better divide control access to he codebase and not unnecessarily give some team members access to code they do not need.
So there are two separate applications, served from the same NodeJS server. The app that is served depends on whether the user is logged in. If they are a guest user, they get one version app, if they are registered user they get a privileged version of the app with more features.
How can I conditionally/dynamically serve static files in Express, so as to say, "if User1 is a guest, serve Application A, otherwise serve Application B"?
If it's just one file you're talking about serving (e.g. app-1.js or app-2.js) then you don't need express.static. You can just handle the request in a normal route handler with res.sendFile like so:
app.get('/get-app', function(req, res) {
if (userIsLoggedIn()) {
res.sendFile('/path-to-logged-in-app.js')
} else {
res.sendFile('/path-to-logged-out-app.js')
}
})
More about res.sendFile here.
If you want to serve multiple files via express.static, you'll need two instances of express.static middleware, and another piece of middleware on top of that to modify the request url depending up on the user's logged in status. Maybe something along the lines of the following:
// Middleware function to modify the request url based on user's status
function checkUser(req, res, next) {
if (userIsLoggedIn()) {
req.url = `/app-1/${req.url}`
} else {
req.url = `/app-2/${req.url}`
}
next()
}
// Set up the middleware stack
app.use(checkUser)
app.use('/app-1', express.static(path.join(__dirname, 'app-1')))
app.use('/app-2', express.static(path.join(__dirname, 'app-2')))
More about writing your own middleware here. You also might want to add some logic in checkUser to only prepend to urls requesting static files (e.g. request urls to /assets/foo.js get the prepend treatment but requests to /api/some-api-function do not).
All that said, I'm not that familiar with Angular but I'd suggest investigating other ways to serve logged in / logged out content. I'm assuming there is some concept of components or "views" in Angular, and it's probably a lot cleaner to just render a "LoggedIn" or "LoggedOut" component/view vs. sending a whole different "app".

How to prevent express server from serving api routes from the static folder

Hi I need some help with how express handles routes.
In setting up my express app, I have something like this:
app.use(express.static('public'));
Next, I mount some api routes:
app.use('/api', myrouter);
app.get('*', function(req, res) {
res.sendFile(path.resolve('public/index.html'));
});
But, when the frontend requests data via an api route, e.g. at 'localhost:3000/api/things', I am seeing in the Express debug logs that at some point (unsure when) it actually tries to serve this request as a static file, like:
send stat "C:\myproject\public\api\things" +230ms
Even though this folder doesn't exist in 'public' and should be solely handled by my api. FYI, the handler for /api/things route is only implemented for the GET method, and does get invoked at some point.
How do I stop express server from also trying to serve api requests from the static folder?
Thanks very much.
Answering my own question... which appears to be a duplicate of this one:
`express.static()` keeps routing my files from the route
So the answer is this one: https://stackoverflow.com/a/28143812/8670745
In short, the app.use() declarations that mount your api routers should appear before the app.use() statements which tell express.static where to serve your static files from. This way, the latter acts as a catchall AFTER api route handling is done. Router engine order matters...
Your answer is misinformed, or rather you've misinterpreted the problem. Your original configuration:
app.use(express.static(__dirname + 'public'));
app.use('/api', myrouter);
Looks absolutely fine because there's no clash between the routes. The threads you've linked too aren't really the same, and I can see why moving the routes in those cases would have worked.
The only thing I'd say is your path to your static folder isn't reliable, you should really use path.join, or actually in your case you can just do express.static('public') - express will infer the folder your app is served from.

Express middleware refrain static from going into next

I have the following function:
app.use('/Framework', express.static(path.resolve(__dirname, 'Framework')));
app.get('/*/*', function (req, res, next) {
...code...
);
The request goes into both the app.use and the app.get. I'd like it that when I try to access a resource with /Framework it just gives it to me as static while ignoring the other functions from below. Yet when accessing static it seems to go in the app.get.
Why is this happening and how can I modify it to get the wanted behavior?
Edit due to comment
I also have a
app.use('/Public', express.static(path.resolve(__dirname, 'Public')));
before which works perfectly as expected.
Requests such as : /Public/0.png work while requests such as /Framework/0.png enter both functions. I have checked the folders names over and over again and they are correct.
Express-static is Express middleware. Middleware is chained together and after a middleware is done executing its code, it calls next(); internally to allow the next middleware to execute. After all middleware is executed, the routes will be executed.
Since express-static calls next(); after its done, the routes will be invoked and checked if any routes match the request. Since your request /Public/0.png matches your route /*/* the route is executed.
My suggestion would be to try an rename your route from /*/* to something more specific.
You could try regex for the route which would explicitly allow any requests matching /*/* but would refuse any routes which have a file extension appended to it (i.e. /*/*.png).
Another (hacky) way would be to check for headersSent inside your /*/* route before executing the code within that route.
A side-note:
Seeing how your route name (e.g. Public and Framework) are the same as your directory names, you could simply do
app.use('/', express.static(path.resolve(__dirname, PARENT_DIRECTORY)));
Where PARENT_DIRECTORY is the parent directory of both Public and Framework. This way
Your directory structure would look like this:
Directory
|
| ---- Public
| ---- Framework
And any requests to Public would end up serving files from your Public directory, same for the Framework directory respectively.

How to GET/POST files in express.static()

I'm a little confused as to how Express serves files.
Currently I have a /public directory to hold client-side resources. I configure Express using
app.use(express.static(__dirname + '/public'));
It was my impression that anything in this directory was public, and that HTTP method urls defaulted /public as the root directory for access (unless otherwise routed manually by Express).
There is no problem using GET on any file in this directory (client-side scripts, images, ect. However, I get 404's when trying to POST files inside this directory. Do I need to manually route all POST requests ala
app.post(route, callback)
Thanks for you help
Connect, and therefore, Express, the static middleware only accepts GET requests. See here.
If you are trying to overwrite files in the public with a POST, you'll want to create a separate route for that.
Connect/Express' static middleware only supports GET and HEAD method:
if ('GET' != req.method && 'HEAD' != req.method) return next();
So, yes, if you want to be able to POST to paths matching static files, you'll need to define the handler(s) yourself.
I find an easy way to post static
app.use(staticPath, function(req, res, next){
if ('POST' != req.method){
next()
}else{
req.method = 'GET'
next()
}
})
app.use(staticPath, express.static('./static'))
It works well, haha

Resources