I have a bunch of html files that I want to serve as static files. However, I need the routes to not have any .html in it.
When the route is
example.com/about
it should serve about.html
The research I did on serving static files seems to suggest that it is not entirely possible. Is that right, or am I missing something?
You need to serve your static file using the router
app.get('/about', function(req, res) {
res.render('about.html');
});
Before that you should setup a middleware for the html rendering engine
Or if you have a lot of static files, you could use a middleware to do it for you
https://www.npmjs.com/package/clean-urls
You can create a short little piece of middleware that will process whichever preset names you configure it for. Anything not in that list will go to your normal routes:
// create static file lookup table for the desired names
var staticFiles = ["about", "index", "home"].reduce(function(obj, item) {
obj["/" + item] = true;
return obj;
}, {});
app.use(function(req, res, next) {
// if the path is found in the lookup table, then
// add ".html" onto the end and get that file from the base directory
// of you could use any source directory for those files
if (staticFiles[req.path]) {
res.sendFile(path.join(__dirname, req.path + ".html"));
return;
}
next();
});
Note: this very carefully only serves specific files so nobody can go snooping around in your file system with other types of URLs.
Here's an example where the files are served from a subdirectory below our base directory named "html":
app.use(function(req, res, next) {
if (staticFiles[req.path]) {
res.sendFile(path.join(__dirname, "html", req.path + ".html"));
return;
}
next();
});
In node v0.12+, you can use the Set object for your list of static routes like this:
var staticFiles = new Set(["/about", "/index", "/home"]);
app.use(function(req, res, next) {
if (staticFiles.has(req.path)) {
res.sendFile(path.join(__dirname, "html", req.path + ".html"));
return;
}
next();
});
If you have a lot of static pages, the ideal way would be to put them up in a single route, lets call it static.js. It can be something like,
module.exports = function () {
var router = require('express').Router();
var pages = {
'about-us': 'path/to/about-us',
'contact-us': 'path/to/contact-us'
};
router.get('/:page', function (req, res, next) {
if (! pages[req.params.page]) {
return next();
}
res.render(pages[req.params.page]);
});
return router;
};
Attach this route to the app, app.use(require('./static')()); and you are good to go.
Try this.
app.use(express.static(path.join(__dirname, 'xyz')));
where xyz is the folder you are trying to make available as static. The folder name is relative to the application root.
Related
I'm trying to get express static working with dynamic subdomain.
Basically http://ratty-doll-4811.localhost:3333 this subdomain is dynamic, and I load static folder based on this subdomain.
My issue is index.html loads but app.js which is in the same directory as index.html, doesn't load.
const subdomain = require('subdomain');
var app = Express()
app.use(subdomain({ base : 'localhost', removeWWW : true }));
app.get('/subdomain/:url', function(req, res, next) {
app.use('', Express.static(path.join(__dirname, 'public')));
res.sendFile('index.html', { root: __dirname + '/public' })
});
Here is the error:
You can't dynamically call app.use() because that will just build up route handlers over and over and they will accumulate indefinitely and be in force for all future requests. You can however, get the request handler from express.static() and call the request handler yourself dynamically.
I don't follow exactly what you're trying to accomplish, but this will show you how you can call it dynamically and then act differently based on whether it found a match of not.
// get express.static() handler
let staticHandler = Express.static(path.join(__dirname, 'public'));
app.get('/subdomain/:url', function(req, res, next) {
staticHandler(req, res, (err) => {
if (err) return next(err);
// it only gets here if the staticHandler didn't find a match and send a response
res.sendFile('index.html', { root: __dirname + '/public' })
});
});
I have a problem with dynamic routing with express.js in my react app. Everything works on localhost but when im deploying i get 400. Here's link: https://shoppyshop.herokuapp.com/item/1
As you see in console there is 400 error. My goal is to fetch data properly from that route that i did setup in my index.js of express.
app.get('/api/item/:id', function (req, res) {
let find = data.filter((el => {
return el.product.id == req.params.id
}))
res.json(find)
console.log('found item')
})
I understand that there is a problem with express. Im probably using wrong method so it wants to fetch icon and rest files from wrong path. Was googling, couldn't find similar problem. How to fix that? Here's component that fetches data:
export default class ItemInfo extends Component {
constructor(props) {
super(props)
this.state = {
data: []
}
}
componentDidMount = () => {
axios.get(`/api/item/${this.props.match.params.id}`)
.then(res => {
const data = res.data;
this.setState({
data,
fetched: true
})
})
}
render() {
console.log(this.props.match.params.id)
return (
<div className="App">
<SideBar pageWrapId={"mainpage"} outerContainerId={"MainPage"} />
<div id="mainpage">
<TopBar />
<More data={this.state.data} fetched={this.state.fetched}/>
<Footer />
</div>
</div>
)
}
}
Here's branch with code that im working on: https://github.com/KamilStaszewski/shoppy/tree/itemrouting/client
My express.js file:
const express = require('express');
const path = require('path');
const data = require('./apiData');
const app = express();
// Serve the static files from the React app
app.use(express.static(path.join(__dirname, 'client/build')));
app.disable('etag');
app.get('/category/:category', function (req, res) {
let find = data.filter((el => {
return el.product.category == req.params.category
}));
res.json(find);
console.log('found category');
});
app.get('/api/item/:id', function (req, res) {
let find = data.filter((el => {
return el.product.id == req.params.id
}))
res.json(find)
console.log('found item')
})
// An api endpoint that returns a short list of items
app.get('/api/data', (req, res) => {
var questions = data
res.json(questions);
console.log('Sent list of items');
});
// Handles any requests that don't match the ones above
app.get('*', (req, res) => {
res.sendFile(path.join(__dirname + '/client/public/index.html'));
});
const port = process.env.PORT || 5000;
app.listen(port);
console.log('App is listening on port ' + port);
UPDATE:
After many commits and pushing I found the answer. I dont know if its right way, but it works. The problem was with:
app.get('*', (req, res) => {
res.sendFile(path.join(__dirname + '/client/public/index.html'));
});
Instead of serving index from build i was serving index from public which had no js inside it. To make it works i changed to:
app.get('*', (req, res) => {
res.sendFile(path.join(__dirname + '/client/build/index.html'));
});
Now it works.
I am pretty sure that this is a problem with how you are serving static files in express.
From the express docs:
app.use('/static', express.static('public'))
Now, you can load the files that are in the public directory from the
/static path prefix.
http://localhost:3000/static/images/kitten.jpg
http://localhost:3000/static/css/style.css
http://localhost:3000/static/js/app.js
http://localhost:3000/static/images/bg.png
http://localhost:3000/static/hello.html
However, the path that you provide to the express.static function is
relative to the directory from where you launch your node process. If
you run the express app from another directory, it’s safer to use the
absolute path of the directory that you want to serve:
app.use('/static', express.static(path.join(__dirname, 'public')))
Your code has the following for serve static:
app.use(express.static(path.join(__dirname, 'client/build')));
So if I am reading this correctly, as I look at your code, I would understand that to be the following:
When you have an request that comes in for /, the rest of the call will search within client/build for the data. In your case, for your get request to /api/item/:id, that would probably be read by your server to try and find a static file in client/build/api/item/:whatevertheitemidis.
Instead, to serve static files, I would perhaps consider putting them in a directory called 'public' or something, and change your serve static to something like:
app.use('/public', express.static(path.join(//Wherever you keep your static files)));
With this all said, I may have been mistaken regarding some of the nuances of express.static so be sure to checkout the documentation. Either way, I hope to at least have pointed you in the right direction. If you comment out your serve static line and make a request with postman, you should see it working as expected.
UPDATE
I took a look at your code and noticed a couple of things:
I just pulled down your code and the endpoints are working. I tried http://localhost:5000/api/data and it provided the expected data.
It looks like your problem with serving static assets -- such as your favicon -- is because of the %PUBLIC_URL%/ part of your favicon url in your html. So far as I can find, there is nothing in your code that would translate that to an actual route. As soon as I turned that to /favicon, everything started working as expected.
I have a sails app and the routes,static assets associated with the app are served from root and it works fine. I would like to add a express middleware so i could serve the routes and static assets in a specific path.
In order to serve the static assets i used the below inside the customMiddleware function in config/http.js,
app.use('/my/new/path', express.static(path.join(__dirname, '../client', 'dist')));
With the above in place i was able to load the static files both from the root as well as from the /my/new/path.
Now when it comes to route i'm not sure how to handle the sails route to load via express middleware using app.use, for example the home route defaults to '/' in my config/routes-client.js, instead of altering the route there i would like to use something like below which we normally use for a typical node/express app,
app.use('/my/new/path', routes); --> where routes is my server routes
Is there a way to add a express middleware to serve sails route on a specific path?
I'm not sure why you'd want to have a prefix added automatically to your custom routes rather than just adding the prefix directly to the routes in the config/routes.js (or config-routes-client.js) file, but here goes:
// config/http.js
middleware: {
reroute: function (req, res, next) {
// Set the prefix you want for routes.
var prefix = '/foo/bar';
// Create a regex that looks for the prefix in the requested URL.
var regex = new RegExp('^' + prefix + '(/|$)');
// If the prefix is found, replace it with /
if (req.url.match(regex)) {
req.url = req.url.replace(regex, '/');
}
// Continue processing the request.
return next();
}
}
Make sure you add reroute to the middleware.order array in config/http.js. Incidentally, this will take care of static files as well.
This is how I do it...
Inside http.js
var express = require('express');
module.exports.http = {
customMiddleware: function (app) {
app.use('/docs', express.static(path.join(sails.config.appPath, '/docs')));
},
... rest of the http.js code...
}
Of course in this example im serving docs folder from root of my app on route /docs... You addapt to your logic...
I'm just experimenting with basic routing in ExpressJS and so far I have two routes:
app.get('/', function(req,res) {
res.render('index');
});
app.get('/pics', function(req,res) {
res.render('pics');
});
When defined like this, in my app.js, everything works fine, but when exported as below, from individual files in my routes subdirectory, I get the error message that a callback function was expected, but an Object undefined was returned.
index.js:
exports.index = function(req,res) {
res.render('index');
});
pics.js
exports.pics = function(req, res) {
res.render('pics');
};
app.js
var routes = require('./routes');
app.get('/', routes.index);
app.get('/pics', routes.pics);
What am I doing wrong in the latter example to break everything?
The index route is working but your pics route isn't because you are trying to import it from index.js.
The route directory has index.js which means that if you do require('./route') you are actually doing require('./route/index'). This happens because index.js has a special meaning in Node.js.
So to get pics working you need to do:
app.get('/pics', require('./routes/pics').pics);
This can be really confusing and is a question that gets asked a lot on the IRC channel.
require('./routes') only loads ./routes/index.js, not ./routes/pics.js. So routes.pics will be undefined.
require() will try to load index.js.
Here is a small coffeescript snippet that you can paste (convert it to js) in index.js. It will autoload all your files(routes) in the current directory:
index.coffee
module.exports = (app) =>
require("fs").readdirSync(__dirname).forEach (file) ->
i = file.lastIndexOf('.')
ext = if (i < 0) then '' else file.substr(i)
if (ext == ".js" or ext == ".coffee") and file != "index"+ext
require("./" + file)(app)
app.js
require("./routes")(app)
someRoutes.js
app.get('/', function(req,res) {
res.render('index');
});
I want to render raw .html pages using Express 3 as follows:
server.get('/', function(req, res) {
res.render('login.html');
}
This is how I have configured the server to render raw HTML pages (inspired from this outdated question):
server
.set('view options', {layout: false})
.set('views', './../')
.engine('html', function(str, options) {
return function(locals) {
return str;
};
});
Unfortunately, with this configuration the page hangs and is never rendered properly. What have I done wrong? How can I render raw HTLM using Express 3 without fancy rendering engines such as Jade and EJS?
What I think you are trying to say is:
How can I serve static html files, right?
Let's get down to it.
First, some code from my own project:
app.configure(function() {
app.use(express.static(__dirname + '/public'));
});
What this means that there is a folder named public inside my app folder. All my static content such as css, js and even html pages lie here.
To actually send static html pages, just add this in your app file
app.get('/', function(req, res) {
res.sendFile(__dirname + '/public/layout.html');
});
So if you have a domain called xyz.com; whenever someone goes there, they will be served layout.html in their browsers.
Edit
If you are using express 4, things are a bit different.
The routes and middleware are executed exactly in the same order they are placed.
One good technique is the place the static file serving code right after all the standard routes.
Like this :
// All standard routes are above here
app.post('/posts', handler.POST.getPosts);
// Serve static files
app.use(express.static('./public'));
This is very important as it potentially removes a bottleneck in your code. Take a look at this stackoverflow answer(the first one where he talks about optimization)
The other major change for express 4.0 is that you don't need to use app.configure()
If you don't actually need to inject data into templates, the simplest solution in express is to use the static file server (express.static()).
However, if you still want to wire up the routes to the pages manually (eg your example mapping '/' to 'login.html'), you might try res.sendFile() to send your html docs over:
http://expressjs.com/api.html#res.sendfile
Have you tried using the fs module?
server.get('/', function(req, res) {
fs.readFile('index.html', function(err, page) {
res.writeHead(200, {'Content-Type': 'text/html'});
res.write(page);
res.end();
});
}
as the document says : 'Express expects: (path, options, callback)' format function in app.engin(...).
so you can write your code like below(for simplicity, but it work):
server
.set('view options', {layout: false})
.set('views', './../')
.engine('html', function(path, options, cb) {
fs.readFile(path, 'utf-8', cb);
});
of course just like 2# & 3# said, you should use express.static() for static file transfer; and the code above not suit for production
First, the mistake you did was trying to use the express 2.x code snippet to render raw HTML in express 3.0. Beginning express 3.0, just the filepath will be passed to view engine instead of file content.
Coming to solution,
create a simple view engine
var fs = require('fs');
function rawHtmlViewEngine(filename, options, callback) {
fs.readFile(filename, 'utf8', function(err, str){
if(err) return callback(err);
/*
* if required, you could write your own
* custom view file processing logic here
*/
callback(null, str);
});
}
use it like this
server.engine('html', rawHtmlViewEngine)
server.set('views', './folder');
server.set('view engine', 'html');
Reference
Official express 2.x to 3.x migration guide
See 'Template engine integration' section in this url
https://github.com/visionmedia/express/wiki/Migrating-from-2.x-to-3.x
After a fresh install of the latest version of Express
express the_app_name
Creates a skeleton directory that includes app.js.
There is a line in app.js that reads:
app.use(express.static(path.join(__dirname, 'public')));
So a folder named public is where the magic happens...
Routing is then done by a function modeled this way:
app.get('/', function(req,res) {
res.sendfile('public/name_of_static_file.extension');
});
*Example:*
An index.html inside the public folder is served when invoked by the line:
app.get('/', function(req,res) {
res.sendfile('public/index.html');
});
As far as assets go:
Make sure the css and javascript files are called from the folder relative to the public folder.
A vanilla Express install will have stylesheets, javascripts, and images for starting folders. So make sure the scripts and css sheets have the correct paths in index.html:
Examples:
<link href="stylesheets/bootstrap.css" rel="stylesheet">
or
<script src="javascripts/jquery.js"></script>
You can render .html pages in express using following code:-
var app = express();
app.engine('html', ejs.__express);
And while rendering, you can use following code:-
response.render('templates.html',{title:"my home page"});
I wanted to do this because I'm creating a boilerplate NodeJS server that I don't want tied to a view engine. For this purpose it's useful to have a placeholder rendering engine which simply returns the (html) file content.
Here's what I came up with:
//htmlrenderer.js
'use strict';
var fs = require('fs'); // for reading files from the file system
exports.renderHtml = function (filePath, options, callback) { // define the template engine
fs.readFile(filePath, function (err, content) {
if (err) return callback(new Error(err));
var rendered = content.toString();
// Do any processing here...
return callback(null, rendered);
});
};
To use it:
app.engine('html', htmlRenderer.renderHtml);
app.set('view engine', 'html');
Source: http://expressjs.com/en/advanced/developing-template-engines.html
Comments and constructive feedback are welcome!
After years a new answer is here.
Actually this approach like skypecakess answer;
var fs = require('fs');
app.get('/', function(req, res, next) {
var html = fs.readFileSync('./html/login.html', 'utf8')
res.send(html)
})
That's all...
Also if EJS or Jade will be used the below code could be used:
var fs = require('fs');
app.get('/', function(req, res, next) {
var html = fs.readFileSync('./html/login.html', 'utf8')
res.render('login', { html: html })
})
And views/login.ejs file contains only the following code:
<%- locals.html %>
app.get('/', function(req, res) {
returnHtml(res, 'index');
});
function returnHtml(res, name) {
res.sendFile(__dirname + '/' + name + '.html');
}
And put your index.html to your root page, of course you could create a /views folder for example and extend returnHtml() function.
You can send file using res.sendFile().
You can keep all html files in views folder and can set path to it in options variable.
app.get('/', (req, res)=>{
var options = { root: __dirname + '/../views/' };
var fileName = 'index.html';
res.sendFile(fileName, options);
});