Serve Static Files on a Dynamic Route using Express - node.js

I want to serve static files as is commonly done with express.static(static_path) but on a dynamic
route as is commonly done with
app.get('/my/dynamic/:route', function(req, res){
// serve stuff here
});
A solution is hinted at in this comment by one of the developers but it isn't immediately clear to me what he means.

Okay. I found an example in the source code for Express' response object. This is a slightly modified version of that example.
app.get('/user/:uid/files/*', function(req, res){
var uid = req.params.uid,
path = req.params[0] ? req.params[0] : 'index.html';
res.sendFile(path, {root: './public'});
});
It uses the res.sendFile method.
NOTE: security changes to sendFile require the use of the root option.

I use below code to serve the same static files requested by different urls:
server.use(express.static(__dirname + '/client/www'));
server.use('/en', express.static(__dirname + '/client/www'));
server.use('/zh', express.static(__dirname + '/client/www'));
Although this is not your case, it may help others who got here.

You can use res.sendfile or you could still utilize express.static:
const path = require('path');
const express = require('express');
const app = express();
// Dynamic path, but only match asset at specific segment.
app.use('/website/:foo/:bar/:asset', (req, res, next) => {
req.url = req.params.asset; // <-- programmatically update url yourself
express.static(__dirname + '/static')(req, res, next);
});
// Or just the asset.
app.use('/website/*', (req, res, next) => {
req.url = path.basename(req.originalUrl);
express.static(__dirname + '/static')(req, res, next);
});

This should work:
app.use('/my/dynamic/:route', express.static('/static'));
app.get('/my/dynamic/:route', function(req, res){
// serve stuff here
});
Documentation states that dynamic routes with app.use() works.
See https://expressjs.com/en/guide/routing.html

Related

Get the URL path in Express.JS (without assets)

here I let you a question that is bothering me a little bit.
I have a middleware in my Express application that logs in a .txt file every path that I request. My code looks like this:
const fs = require('fs');
module.exports = function (req, res, next) {
fs.writeFileSync('log.txt', req.originalUrl + '\n');
next();
}
It works fine, but logs every resource from the request (the path of images, the path of css files, the path of js files) and I only want, the path present in the browser address bar.
Anyone knows how can I get that?
you need to chose wisely the position of your middleware.
to achieve what you say you need to put your own midleware after assets middleware
exemple
app.use(express.static('public'));
app.use(function (req, res, next) {
fs.writeFileSync('log.txt', req.originalUrl + '\n');
next();
})
app.get('/', () =>{
...
})
by doing this, your middleware will only be called if the path is not an asset.

Different ways to render static assets using express middleware

Which of the following method is preferable to render the static assets. Consider that only helpPage.html is the only file exist in the public directory
Method 1:
app.use(express.static(__dirname + '/public'))
Method2:
app.use((req, res) => {
res.render(__dirname + '/public/helpPage.html');
})
If helpPage.html is the only static file you will deliver, I propose a third option:
app.get('/helpPage.html', (req, res)=>{
res.sendFile( __dirname + '/public/helpPage.html');
});
I don't see the purpose of using app.use here.
Additionally you may want to use path.join:
const path = require('path');
app.get('/helpPage.html', (req, res)=>{
res.sendFile( path.join(__dirname, '/public/helpPage.html') );
});
This ensures that the paths are properly joined regardless of what machine you are running on.

having trouble referencing separate routes file with express-subdomain

I'm trying to add a subdomain to an existing node app using Express 3.0 and express-subdomain.
(I've added the subdomain to my hosts file and that's working fine.)
The current app has all the routes in a separate routes.js file in the same directory as the main file, and it's called like this:
var routes = require("./routes")
//other stuff
routes.routeList(app);
I've tried a bunch of different ways to use the
app.use(subdomain('test-developer', [router]));
syntax and I can't figure it out.
I've tried
var router = require("./routes.js");
app.use(subdomain('test-developer', router));
and I get an error like "The second parameter must be a function that handles fn(req, res, next) params."
here's some more of the code:
//the developerRoutes.js file
var express = require('express');
exports.developerRouteList = function(app) {
var devRouter = express.Router();
devRouter.get('/', function(req, res) {
res.send('hi!');
});
}
and from the main.js file:
var developerRoutes = require('./developerRoutes');
//...
var app = express();
app.use(subdomain('test-developer', developerRoutes.developerRouteList));
//force https with this:
app.enable('trust proxy');
app.configure(function() {
app.use(function (req, res, next){
var hostname = ( req.headers.host.match(/:/g) ) ? req.headers.host.slice( 0, req.headers.host.indexOf(":") ) : req.headers.host
console.log(hostname)
if ((hostname === 'localhost') || (hostname === 'test-developer.localhost') || req.secure) {
// request was via https, so do no special handling
next();
} else {
// request was via http, so redirect to https
res.redirect('https://' + req.headers.host + req.url);
}
});
app.use(express.bodyParser());
app.use(express.cookieParser('secret'));
app.use(function(req, res, next){
session = require("./routes/includes/session.js");
next();
});
app.use(express.static('./public'));
app.use(app.router);
});
app.engine('ejs', engine);
app.set('views',__dirname + '/views');
app.set('view engine', 'ejs');
multiLess.configure(__dirname + '/static/less/', parentDirectory + 'public/css/',['main.less'],0);
routes.routeList(app);
Any ideas on how to sort this out?
author of express-subdomain 👋
I've answered this question in a previous issue - see https://github.com/bmullan91/express-subdomain/issues/4. I should probably add that to the readme as there is still a few folk using v3.
Based on your posted code, I would try the following steps:
In developerRoutes.js, exports.developerRouteList = function(app) should be module.exports = devRouter, and it should be after var devRouter = express.Router(). Unless you're putting multiple routers in developerRoutes.js, you don't need to make a sub-object named developerRouteList within the module.exports object. Also, it doesn't look like you're using the app instance you pass in to developerRoutes.js so it should be okay to change this.
In main.js, now you can try app.use(subdomain('test-developer', developerRoutes));

Using express.static middleware in an authorized route

I'm using node with express and passportjs to restrict access to files located in a private folder. I have reduced my code to the following.
Everything in the public static folder works great but route targeting the private folder through the use of the staticMiddleware returns 404 errors.
var express = require('express')
, util = require('util');
var app = express.createServer();
var staticMiddleware = express.static(__dirname + '/private');
app.configure(function() {
app.use(app.router);
app.use(express.logger('dev'));
app.use('/public',express.static(__dirname + '/public'));
});
app.get('/private/:file', function(req, res, next){
console.log('about to send restricted file '+ req.params.file);
staticMiddleware(req, res, next);
});
app.listen(16000);
I was using the following references that seems to work for others, so I must be missing something.
It won't work for me showing only 404 responses for the content located in the private area.
Node.js module-specific static resources
NodeJS won't serve static files, even when using express.static
Redirecting to a static file in express.js
I could have sworn I had this working before, maybe it was broken in a new version of something.
Node v0.8.1
npm 1.1.12
express#2.5.11
connect#1.9.2
sheesh staring at me the whole time
app.get('/private/:file', function(req, res, next){
console.log('about to send restricted file '+ req.params.file);
req.url = req.url.replace(/^\/private/, '')
staticMiddleware(req, res, next);
});
Edit 11-29-2014
So after someone posted to the question I came back to this answer to find that even though I mention passportjs I never showed how I ended up using this function.
var staticMiddlewarePrivate = express['static'](__dirname + '/private');
app.get('/private/*/:file', auth.ensureAuthenticated, function(req, res, next){
console.log('**** Private ****');
req.url = req.url.replace(/^\/private/, '');
staticMiddlewarePrivate(req, res, next);
});
You can also add express.static(__dirname + '/private'); to your app.config.
app.configure(function() {
app.use(app.router);
app.use(express.logger('dev'));
app.use('/public',express.static(__dirname + '/public'));
app.use('/private',express.static(__dirname + '/private'));
});
The private path middleware would be executed anytime a path began with private.

Is it possible to set a base URL for NodeJS app?

I want to be able to host multiple NodeJS apps under the same domain, without using sub-domains (like google.com/reader instead of images.google.com). The problem is that I'm always typing the first part of the url e.g. "/reader" in Express/NodeJS.
How can I set up an Express app so that the base URL is something.com/myapp?
So instead of:
app.get("/myapp", function (req, res) {
// can be accessed from something.com/myapp
});
I can do:
// Some set-up
app.base = "/myapp"
app.get("/", function (req, res) {
// can still be accessed from something.com/myapp
});
I'd also like to configure Connect's staticProvider to behave the same way (right now it defaults to serving static files to something.com/js or something.com/css instead of something.com/myapp/js)
The express router can handle this since 4.0
http://expressjs.com/en/api.html#router
http://bulkan-evcimen.com/using_express_router_instead_of_express_namespace.html
var express = require('express');
var app = express();
var router = express.Router();
// simple logger for this router's requests
// all requests to this router will first hit this middleware
router.use(function(req, res, next) {
console.log('%s %s %s', req.method, req.url, req.path);
next();
});
// this will only be invoked if the path ends in /bar
router.use('/bar', function(req, res, next) {
// ... maybe some additional /bar logging ...
next();
});
// always invoked
router.use(function(req, res, next) {
res.send('Hello World');
});
app.use('/foo', router);
app.listen(3000);
Previous answer (before express 4.0) :
The express-namespace module (dead now) used to do the trick :
https://github.com/visionmedia/express-namespace
require('express-namespace');
app.namespace('/myapp', function() {
app.get('/', function (req, res) {
// can be accessed from something.com/myapp
});
});
At the moment this is not supported, and it's not easy to add it on your own.
The whole routing stuff is buried deep inside the server code, and as a bonus there's no exposure of the routes them selfs.
I dug through the source and also checked out the latest version of Express and the Connect middleware, but there's still no support for such functionality, you should open a issue either on Connect or Express itself.
Meanwhile...
Patch the thing yourself, here's a quick and easy way with only one line of code changed.
In ~/.local/lib/node/.npm/express/1.0.0/package/lib/express/servers.js, search for:
// Generate the route
this.routes[method](path, fn);
This should be around line 357, replace that with:
// Generate the route
this.routes[method](((self.settings.base || '') + path), fn);
Now just add the setting:
app.set('base', '/myapp');
This works fine with paths that are plain strings, for RegEx support you will have to hack around in the router middleware yourself, better file an issue in that case.
As far as the static provider goes, just add in /mypapp when setting it up.
Update
Made it work with RegExp too:
// replace
this.routes[method](baseRoute(self.settings.base || '', path), fn);
// helper
function baseRoute(base, path) {
if (path instanceof RegExp) {
var exp = RegExp(path).toString().slice(1, -1);
return new RegExp(exp[0] === '^' ? '^' + base + exp.substring(1) : base + exp);
} else {
return (base || '') + path;
}
}
I only tested this with a handful of expressions, so this isn't 100% tested but in theory it should work.
Update 2
Filed an issue with the patch:
https://github.com/visionmedia/express/issues/issue/478
Just to update the thread, now with Express.js v4 you can do it without using express-namespace:
var express = require('express'),
forumRouter = express.Router(),
threadRouter = express.Router(),
app = express();
forumRouter.get('/:id)', function(req, res){
res.send('GET forum ' + req.params.id);
});
forumRouter.get('/:id/edit', function(req, res){
res.send('GET forum ' + req.params.id + ' edit page');
});
forumRouter.delete('/:id', function(req, res){
res.send('DELETE forum ' + req.params.id);
});
app.use('/forum', forumRouter);
threadRouter.get('/:id/thread/:tid', function(req, res){
res.send('GET forum ' + req.params.id + ' thread ' + req.params.tid);
});
forumRouter.use('/', threadRouter);
app.listen(app.get("port") || 3000);
Cheers!
I was able to achieve this using a combination of express-namespace for the routes and a fix from the below google group discussion for the static assets. This snippet will treat a request to /foo/javascripts/jquery.js like a request to /javascripts/jquery.js:
app.use('/foo', express.static(__dirname + '/public'));
Source:
https://groups.google.com/forum/#!msg/express-js/xlP6_DX6he0/6OTY4hwfV-0J
I know this is a very old question but Express has changed a lot since most these answers were posted so I thought I'd share my approach.
You can, of course, use Routers with Express 4 to group together related functionality behind a particular path. This is well documented and has already been covered by other answers.
However, it is also possible to mount an entire application at a particular path. As an example, let's assume our application (the one we want to host at /myapp) looks like this, in a file called myapp.js:
var express = require('express'),
path = require('path'),
app = express();
app.use(express.static(path.join(__dirname, 'public')));
app.get('/hello', function(req, res) {
res.send('Hello');
});
// Lots of other stuff here
exports.app = app;
In our main js file we could then mount this whole application at the path /myapp:
var express = require('express'),
app = express(),
myApp = require('./myapp').app;
app.use('/myapp', myApp);
app.listen(3000);
Note that we've created two applications here, one mounted on the other. The main application could have further sub-apps mounted at different paths as required.
The code in myapp.js is completely independent of where it was mounted. It's similar to the structure used by the express-generator in that regard.
Some documentation about sub-apps can be found here:
https://expressjs.com/en/4x/api.html#app.mountpath
https://expressjs.com/en/4x/api.html#app.onmount
There are also reliability issues. If reliability is important, a common solution is to use a front-end reverse HTTP proxy such as nginx or HAProxy. They both use single-thread evented architecture and are thus very scalable.
Then you can have different node processes for different subsites, and if one site fails (uncaught exception, memory leak, programmer error, whatever) the rest of sub-sites continue to work.
I was looking for this feature but for API routes, not for static files. What I did was that when I initialized the router, I added the mount path. So my configuration looks like this
//Default configuration
app.configure(function(){
app.use(express.compress());
app.use(express.logger('dev'));
app.set('json spaces',0);
app.use(express.limit('2mb'));
app.use(express.bodyParser());
app.use('/api', app.router); // <---
app.use(function(err, req, res, callback){
res.json(err.code, {});
});
});
Notice the '/api' when calling the router

Resources