I'm currently working on an application built with Express (Node.js) and I want to know what is the smartest way to handle different robots.txt for different environments (development, production).
This is what I have right now but I'm not convinced by the solution, I think it is dirty:
app.get '/robots.txt', (req, res) ->
res.set 'Content-Type', 'text/plain'
if app.settings.env == 'production'
res.send 'User-agent: *\nDisallow: /signin\nDisallow: /signup\nDisallow: /signout\nSitemap: /sitemap.xml'
else
res.send 'User-agent: *\nDisallow: /'
(NB: it is CoffeeScript)
There should be a better way. How would you do it?
Thank you.
Use a middleware function. This way the robots.txt will be handled before any session, cookieParser, etc:
app.use('/robots.txt', function (req, res, next) {
res.type('text/plain')
res.send("User-agent: *\nDisallow: /");
});
With express 4 app.get now gets handled in the order it appears so you can just use that:
app.get('/robots.txt', function (req, res) {
res.type('text/plain');
res.send("User-agent: *\nDisallow: /");
});
1. Create robots.txt with following content :
User-agent: *
Disallow: # your rules here
2. Add it to public/ directory.
3. If not already present in your code, add:
app.use(express.static('public'))
Your robots.txt will be available to any crawler at http://yoursite.com/robots.txt
Looks like an ok way.
An alternative, if you'd like to be able to edit robots.txt as regular file, and possibly have other files you only want in production or development mode would be to use 2 separate directories, and activate one or the other at startup.
if (app.settings.env === 'production') {
app.use(express['static'](__dirname + '/production'));
} else {
app.use(express['static'](__dirname + '/development'));
}
then you add 2 directories with each version of robots.txt.
PROJECT DIR
development
robots.txt <-- dev version
production
robots.txt <-- more permissive prod version
And you can keep adding more files in either directory and keep your code simpler.
(sorry, this is javascript, not coffeescript)
Here is what I use
router.use('/robots.txt', function (req, res, next) {
res.type('text/plain')
res.send(
`User-agent: *
Disallow: /admin`);
});
For choosing the robots.txt depending the environment with a middleware way:
var env = process.env.NODE_ENV || 'development';
if (env === 'development' || env === 'qa') {
app.use(function (req, res, next) {
if ('/robots.txt' === req.url) {
res.type('text/plain');
res.send('User-agent: *\nDisallow: /');
} else {
next();
}
});
}
This is what I did on my index routes. You can just simply write down in your codes what I does given down below.
router.get('/', (req, res) =>
res.sendFile(__dirname + '/public/sitemap.xml')
)
router.get('/', (req, res) => {
res.sendFile(__dirname + '/public/robots.txt')
})
I use robots.txt as a normal file for Prod, and a middleware for other envs.
if(isDev || isStaging){
app.use('/robots.txt', function (req, res) {
res.type('text/plain');
res.send("User-agent: *\nDisallow: /");
});
}
app.use(express.static(path.join(__dirname, 'public')));
Focusing more on the most convenient and simple solution instead of the "best" or "smartest". I simply added the following to the server.ts file.
server.get('/robots.txt', function (req, res) {
res.type('text/plain');
res.send("User-agent: *\nAllow: /");
})
What this does is create a robots.txt file on the fly and sends it whenever the /robots.txt file is called for.
Now to get this to work, the code fragment must be placed before the other server.get function calls (so it takes priority). I'm implementing Express with Angular, for which the full code fragment for me ended up being:
export function app(): express.Express {
const server = express();
const distFolder = join(process.cwd(), 'dist/sophisticatedPrimate/browser');
const indexHtml = existsSync(join(distFolder, 'index.original.html')) ? 'index.original.html' : 'index';
// Our Universal express-engine (found # https://github.com/angular/universal/tree/main/modules/express-engine)
server.engine('html', ngExpressEngine({
bootstrap: AppServerModule,
}));
server.set('view engine', 'html');
server.set('views', distFolder);
server.get('/robots.txt', function (req, res) {
res.type('text/plain');
res.send("User-agent: *\nAllow: /");
})
// Example Express Rest API endpoints
// server.get('/api/**', (req, res) => { });
// Serve static files from /browser
server.get('*.*', express.static(distFolder, {
maxAge: '1y'
}));
// All regular routes use the Universal engine
server.get('*', (req, res) => {
res.render(indexHtml, { req, providers: [{ provide: APP_BASE_HREF, useValue: req.baseUrl }] });
});
return server;
}
app.use(express.static('public'))
app.use('/images', express.static('public/images'))
app.use('/videos', express.static('public/videos'))
Related
I'm really new to node/express and I'm trying to understand how sending static files work. I managed to serve my index file, but I cannot serve other files as response of a GET request.
app.use(express.static(path.join(__dirname, '/client/build')))
app.get('/', (req, res) => {
res.sendFile(path.resolve(__dirname, '.', 'client/build/', 'index.html'))
res.end()
})
app.get('/portfolio', (req, res) => {
const person = req.query.name
var filePath = __dirname + '/client/build/' + person + '/' + 'index.html'
res.sendFile(filePath)
res.end()
})
I found similar questions but nothing seems to work.
The request I send is:
fetch(`portfolio?name=${who}`)
There's a few problems with your code. One of them is that you end the request before the file is even done sending and another problem is that you're not using the res.sendFile method properly.
try something like this:
app.get('/', (req, res) => {
const fileDirectory = path.resolve(__dirname, '.', 'client/build/');
res.sendFile('index.html', {root: fileDirectory}, (err) => {
res.end();
if (err) throw(err);
});
})
app.get('/portfolio', (req, res) => {
const person = req.query.name
const fileDirectory = __dirname + '/client/build/' + person + '/';
res.sendFile('index.html', {root: fileDirectory}, (err) => {
res.end();
if (err) throw(err);
});
})
I don't recommend throwing an error whenever you get one but it should at least give you an idea how you can get this to work.
I finally solved it by using this on the server:
app.use( express.static(path.join(__dirname, 'client/build/person1')))
app.use( express.static(path.join(__dirname, 'client/build/person2')))
and calling it in the view like this:
<a href='/person1'></a>
<a href='/person2'></a>
As it seems, express.static resolves the paths on its own, so you don't need to handle the request yourself to serve the static files.
If this is not a good solution please coment
express.static is handling the root url request.
e.g. I want to do a redirect in express from https://example.com to https://example.com/dashboard.
Check the cases below, first one works, second does not. I expect the second to work too. Anyone knows why?
Case 1 (works)
app.get('/', (req, res, next) => {
res.redirect('/dashboard');
})
app.use(express.static(path.join(__dirname, 'dist')))
app.get('/dashboard', (req, res, next) => {
//do stuff
})
Case 2 (does not work for me)
app.use(express.static(path.join(__dirname, 'dist')))
//request doesn't come here
app.get('/', (req, res, next) => {
res.redirect('/dashboard')
})
app.get('/dashboard', (req, res, next) => {
//do some stuff
})
That would happen if there's a file dist/index.html, because that's what express.static() would look for when retrieving a directory (in this case /).
You can turn that behaviour off like this:
app.use(express.static(path.join(__dirname, 'dist'), { index : false }))
Documented here: http://expressjs.com/en/4x/api.html#express.static
I'm using Expressjs and i18n for my node js application to manage the multilanguage.
Here is my i18n configuration :
i18n.js
var i18n = require('i18n');
i18n.configure({
locales:['en', 'fr'],
directory: __dirname + '/../locales',
defaultLocale: 'en',
cookie: 'lang',
});
module.exports = function(req, res, next) {
i18n.init(req, res);
res.locals.__= res.__;
var current_locale = i18n.getLocale();
return next();
};
server.js
var i18n = require('./configs/i18n');
...
app.use(i18n);
Actually if I want to change the locale settings I have to do that for each routes :
app.get('/index', function(req, res){
res.setLocale('fr')
res.render('pages/index');
});
It is possible to use setLocale() once and it will permanently change the locale ?
And what is the best practice ? Should I specify the language inside my routes everytime ? E.g :
app.get('/:locale/index', function(req, res){
res.setLocale(req.params.locale)
res.render('pages/index');
});
app.get('/:locale/anotherroute', function(req, res){
res.setLocale(req.params.locale)
res.render('pages/anotherroute');
});
Or I have to store the locale in my database for each user ?
You can use middlewares to avoid the repetition (place this code before your router) :
// Fixed locale
app.use(function (req, res, next) {
res.setLocale('fr');
next();
});
// Locale get by URL parameters
app.use(function (req, res, next) {
if (req.params && req.params.locale){
res.setLocale(req.params.locale);
}
next();
});
Personally, I prefer to store the local setting in database, it avoids to weighing the request with not necessary data.
Another solution is to set the language with HTTP headers Content-Language and Accept-Language and get it with req.acceptsLanguage().
I have a basic express app and I want to serve one file (after performing some logic) for the default route of /.
Unfortunately I can't use
app.use(function (res, res, next){
*logic here*
res.sendFile(filepath);
});
express.static()
because that will intercept every request and send the filepath for every request.
Is there another way of doing this?
It's enough to check the URI part of url and if it's / then send file.
Check this:
app.use(function (req, res, next) { // first must be Your middleware
if(req.path == '/') {
return res.sendFile('some file');
}
next();
});
app.use(express.static('public')); // and after it You can attach static middleware
or:
app.use(express.static('public'));
app.all('/', function (req, res) {
res.sendFile(filePath);
});
var regexp = /^\/\w+/
app.use(function (req, res, next){
if(!regexp.test(req.path)){
res.sendFile(filepath);
}
});
express.static()
this may work comment your requirement
My code:
var i18n = require("i18n");
i18n.configure({
locales: ['en', 'ru'],
defaultLocale: 'en',
directory: __dirname + '/locales',
cookiename: 'locale'
});
app.configure(function () {
app.use(i18n.init);
})
app.get('/:locale', function (req, res) {
res.cookie('locale', req.params.locale);
i18n.setLocale(req.params.locale);
res.redirect('/');
});
The problems are two:
In template does not work output through
__("Name Key")
When the transfer is not directly through value, do not change text. There are all languages files
res.render('index', {name: res.__('name') });
But do not switch languages
/* ----- */
The resulting code:
var i18n = require("i18n");
i18n.configure({
locales: ['en', 'ru'],
defaultLocale: 'ru',
directory: __dirname + '/locales'//,
cookiename: 'locale'
});
app.use(function (req, res, next) {
res.locals.__ = res.__ = function() {
return i18n.__.apply(req, arguments);
};
next();
});
app.get('/i18n/:locale', function (req, res) {
res.cookie('locale', req.params.locale);
i18n.setLocale(req.params.locale);
if (req.headers.referer) res.redirect(req.headers.referer);
else res.redirect("/");
});
This works
In your templates, assuming that you are using JADE you must embrace with #{}, like #{__("Your key")}
I didn't understand your second question, mind rephrasing?
Anyways, if you wanna use i18n from a controller, you must do: res.render('index', {name: res.i18n.__('name') });
When switching the language you must:
1- save user preference anywhere (session or cookies, for example)
app.get("/i18n/:locale", setLocale);
function setLocale(req, res, next){
req.session.locale = req.params.locale;
if(req.headers.referer) res.redirect(req.headers.referer);
else res.redirect("/");
}
2- re-apply this change at every request (simply use a middleware):
var app = require("express")();
app.use(function(req, res, next){
if(req.session.locale) //check if user has changed i18n settings
req.i18n.setLocale(req.session.locale);
})
app.get("/", function(req, res, next){
res.render('index', {name: res.i18n.__('name') });
});
app.listen(8000);
Based on the answer from #renatoargh, I had to make some modifications, but here is the final block that seemed to get things working for me.
// configure i18n
i18n.configure({
locales : [
'en',
'zh'
],
directory : __dirname + '/locales'
});
// configure app
app.configure(function () {
// initialize session support
app.use(express.cookieParser());
app.use(express.cookieSession({secret: uuid.v4()}));
// initialize i18n
app.use(i18n.init);
// set locale (on every request), if session locale exists
// otherwise use default browser setting
app.use(function (req, res, next) {
// check if user has changed i18n settings
if (req.session.locale) {
i18n.setLocale(req, req.session.locale);
}
next();
});
});
// allow MANUAL locale selection
app.get("/i18n/:locale", function (req, res) {
req.session.locale = req.params.locale;
// go back to referrer OR root (/)
res.redirect('back');
});
NOTE in the the configuration, I've set the directory. For some reason this was necessary, even though the docs state that its the default.
Also note that I'm using secret: uuid.v4(), but you can just hard-code this value for persistence across server restarts.