I am trying to get the static files (images, js, css) which are all inside the public folder to be available to all my routes. I am able to ONLY load them to the index.html. The management.html files does not have access to the static files, I am getting 404 error on the Chrome console.
Folder Structure:
-app/
---index.html
---management.html
---public/
------css/
------js/
------img/
server.js:
const express = require('express');
const path = require('path');
var app = express();
app.use(express.static(path.join(__dirname, 'public')));
app.get('/', function(req, res) {
res.sendFile('index.html', {root: __dirname })
});
app.get('/management', function(req, res) {
res.sendFile('management.html', {root: __dirname })
});
app.listen(3000);
Inside your HTML files you are probably referring to the relative paths of the static files. For example: <img src="img/example.png">. This link will be based on your current location.
When your current location is http://example.com/ (index.html), then it will refer to http://example.com/img/example.png, where the file is probably located.
But when your location is http://example.com/management (management.html), it refers to http://example.com/management/img/example.png, which will return a 404.
Using the absolute path of this file will solve this problem. You can do this by putting a / in front of the relative path: <img src="/img/example.png">. Alternatively you could use a relative path in management.html like this: <img src="../img/example.png">.
Related
I have an express app and the static files are not working for every route.
When I get to '/', the static styles and images work perfectly when index.ejs is rendered.
When I get '/efbhew', a route that doesn't exist, 404.ejs renders ok.
When I get '/asdw/feff' or other routes like '/df/fg/dfgdfg/sfgd', the static styles and images do not work. Is there something else I have to do in order for it to work?
Below is my code.
const app = express();
app.use(express.static(path.join(__dirname, '../public')));
app.set('views', path.join(__dirname, '../views'));
app.set('view engine', 'ejs');
app.use(cors());
app.use(express.json());
app.get('/', async (req, res) => {
const posts = await Post.find({}).limit(7).sort({ createdAt: -1 }).exec();
res.render('index.ejs', {
latestPosts: posts
});
});
app.get('*', (req, res) => {
res.render('404.ejs');
});
This is the structure of my folders:
I tried changing the path of the public folders to go deeper since when I got to '/asdf/wfds/sdfsd', the image source is 'http://localhost:3000/asdf/wfds/img/logo.png' and it cant find it. But that still didn't work.
When an ejs page at /df/fg/dfgdfg/sfgd references a static file, as in
<img src="img/logo.png"/>
the URL is relative to the URL of the ejs page, so in this example it would be /df/fg/dfgdfg/img/logo.png (note the last segment sfgd is dropped, that's why it works for a page at /efbhew).
But the image exists only as /img/logo.png. Therefore, you should always address it with a path that starts with a slash, as in
<img src="/img/logo.png"/>
I have been having a lot of trouble with serving css using express js. I finally figured out how, but I'm a bit confused why my new code works, but my old code doesn't. This is my new code that does work:
const express = require('express');
const path = require('path');
const app = express();
const port = process.env.PORT || 5010;
console.log(__dirname)
app.use('/public', express.static('public'));
app.set('views', path.join(__dirname, 'views'))
app.get('/', (req, res) => {
res.sendFile(path.join(__dirname, 'views', 'home.html'));
});
app.listen(port, () => {
console.log(`Server started on port ${port}`);
});
My file system looks like this:
index.js
public
css
home.css
views
home.html
Originally instead of having:
app.use('/public', express.static('public'));
I had:
app.use(express.static(path.join(__dirname, 'public')));
Why does the second version work, but the first one doesn't? What is the purpose of the first parameter in the second version? Also, just in case it makes a difference, I'm coding on replit.com.
When using 1 parameter
app.use(express.static(path.join(__dirname, 'public')));
This code serve files in the "public" subdirectory of the current directory. The URL to access the file at public/css/home.css is : http://localhost/css/home.css
When using 2 parameters
app.use('/public', express.static('public'));
This code also serve files in the "public" subdirectory of the current directory, with a virtual path prefix "/public". So, the URL to access the file at public/css/home.css is : http://localhost/public/css/home.css
We can change the first parameter to anything, for example, if we have :
app.use('/static', express.static('public'));
Then the URL to the same file becomes : http://localhost/static/css/home.css.
You can find more information from the official document here
I'm trying to only use html and render pages from my express server. I keep getting the error
No default engine was specified and no extension was provided.
I have specified the dirname in app.js and I am telling the server to render by using the dirname in my router. I'm not really sure what's holding me up? Can someone provide some insight?
app.js ( I have removed import statements that aren't relevant)
var app = express();
app.use(express.static(__dirname + '/public')); //setting static file directory
//Store all HTML files in view folder.
module.exports = app;
here's my index router where I am calling render on the pages
var express = require('express');
var router = express.Router();
const path = require('path');
/* GET home page. */
router.get('/', function(req, res, next) {
res.render('main', { title: 'Express' });
});
/* GET styles page. */
router.get('/style', function(req, res, next) {
res.render('styles', { title: 'styles' });
});
/* GET styles page. */
router.get('/style',function(req,res){
res.sendFile(path.join(__dirname+'/style.html'));
});
module.exports = router;
If you don't have a renderer like Handlebars, you can't call res.render as far as I know. If you are serving static views you don't need a renderer anyways, you just specify the folder where the static files are located at.
This means after you have specified your static folder, you will be able to access the files by having their filename in the route. Express' documentation on static files. You don't need routes to send the files.
Example
CodeSandbox Example
src
|- view
| |- hello.html
|- index.js
index.js
const express = require("express");
//create a server object:
const app = express();
//Serve all files inside the view directory, path relative to where you started node
app.use(express.static("src/view/"));
app.listen(8080, function() {
console.log("server running on 8080");
}); //the server object listens on port 8080
module.exports = app;
You will now see hello.html on the /hello.html route. Any other file will also be visible under its name.
So I have server.js file that imports a router
const path = require("path");
const express = require("express");
const app = express();
const PORT = 8080;
app.use(express.urlencoded({ extended: true }));
app.use(express.json());
app.use(require('./app/routing/htmlRoutes'));
The router looks like this
const path = require("path");
var express = require('express');
var router = express.Router();
router.get('/', function(req, res) {
res.sendFile('home.html', { root: path.join(__dirname, '../public') });
});
router.get('/survey', function(req, res) {
res.sendFile('survey.html', { root: path.join(__dirname, '../public') });
});
module.exports = router;
It does work! It renders html pages, however those html pages have css stylesheets hooked up to them and located in the same directory, but they render as blank html sheets (unstyled)...
How do I make them render with css stylesheets taken into account?
When the browser encounters the style reference of the loaded html file, it tries to load the file specified in the src attribute. Now your server script doesn't have a route for that. It will load the css if you add a route for that specific css file. However as Irshad said, the standard way to do this is to add a route for all the static files.
app.use(express.static("public"))
Right now, you are only sending home.html everytim the root is requested.
Change your code to read the requested file from the req and serve that file whatever it may be.
Why not set the middleware to serve static files (css, html) from public folder app.use(express.static("public"))
See the working example
I've created a webpage to use it locally. I have a ton of routes like the ones shown below -- 31 .ejs files and 3 .html files. (They are all in the same "views" folder).
//app.js - using node and express
app.get('/page1', function(req, res){
res.render('page1');
});
app.get('/page2', function(req, res){
res.sendFile('views/page2.html', { root: __dirname });
});
I use an app.get for each and every one of these files. I've had a feeling it wasn't DRY code, and so now I'm trying to figure out a more elegant and optimal way to achieve the same result.
I know that many res.sendFile(); could be replaced with a single express.static() middleware statement. I usually use express.static() on a "public" folder which I use to save all my css files -- like this app.use(express.static(path.join(__dirname, 'public')));. But I still don't see how I could use this to simplify all my res.sendFile().
As for the many res.render(); routes, I know that if I don't pass any customized data I could probably replace them with a single middleware that handles either a whole directory of template files (and their corresponding routes) or a list of files. I just don't know how I would do that.
Any help is very much appreciated, thanks!!
[UPDATE]
richie node_modules public
css files, images, etc views
partials
all partial files programmingPublic
all ejs files from a same topic other files (html & other ejs) appjs
packagejson package-lockjson
const express = require('express');
const bodyParser = require('body-parser');
const path = require('path');
const app = express();
// Body Parser Middleware
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({extended: false}));
// engine
app.set("view engine", "ejs");
// Set static path
app.use(express.static(path.join(__dirname, 'public')));
const fs = require('fs');
function renderStatic(dir) {
return function(req, res, next) {
let target = path.join(dir, req.path);
fs.access(target, fs.constants.R_OK, function(err) {
if (err) {
// file not found, just move on
next();
} else {
res.render(target);
}
});
}
}
app.use(renderStatic(path.join(__dirname, "views/programmingPublic")));
Below is the format of my side-menu: (all these files are inside "programmingPublic" folder)
Programming
<li>C</li>
<li>C++</li>
<li>Python</li>
<li>JavaScript</li>
<li>PHP</li>
If you have a bunch of pages that need to call res.render(), but aren't passing custom options to each render, then you could isolate all those templates in their own directory and then use some middleware like this:
const path = require('path');
const fs = require('fs');
function renderStatic(dir, options) {
const regex = /^\.|\.\.|\/\.|\\\./;
options = options || {};
return function(req, res, next) {
let target = path.join(dir, req.path);
if (options.ext && !path.extname(target)) {
target = target + options.ext;
}
// don't allow leading dot or double dot anywhere in the path
if (regex.test(target)) {
next();
return;
}
fs.access(target, fs.constants.R_OK, function(err) {
if (err) {
// file not found, just move on
next();
} else {
res.render(target);
}
});
}
}
app.use(renderStatic(path.join(__dirname, "renderPublic"), {ext: ".ejs"}));
Note, you must isolate these template files in their own directory so that other files are not found there.
For safety completeness, this code also needs to filter out . and .. items in the path like express.static() does to prevent an attacker from going up your directory hierarchy to get access to other files than those in the render static directory.
Then, for the routes you are using res.sendFile() and no other logic, just isolate those HTML files in their own directory and point express.static() at that directory. Then, the express.static() middleware will find a matching HTML file in that directory and do res.sendFile() for you automatically, exactly the same as it does for your CSS files.