I've been playing with express js lately and I'm loving it. My problem is that when I submit an invalid form a validation error message appears but when I reload the page the error message is still displaying I think the error message is being cached. Is there a way to disable caching in jade / swig and also is there a way to just specify which pages to cache and not to cache(like the form)?
sample code:
filename: index.js
// app.get('/form', index.getForm)
exports.getForm = function(req, res){
res.render('form');
}
// app.post('/form', index.postForm)
exports.postForm = function(req, res){
// express form validation goes here ...
if(errors){
res.locals.errors = errors;
res.locals.form = req.body;
exports.getForm(req, res);
}else{
res.redirect('/welcome');
}
}
and here's my jade
filename: form.jade
if error.email
.error=error.email.msg
input#email(type='email', name='email', value='#{form.email}')
else
input#email(type='email', name='email', value='#{form.email}')
Theres no cache problems unless you are running express in production mode.
It looks like when you press F5, after the form error, your request is re-submiting the same form via post (with the error).
Option 1:
AJAX <3
Option 2:
Use 2 diferent jade templates, one for the exports.postForm with
errors and another for the exports.getForm. Something like:
exports.getForm = function(req, res) {
res.render('form');
}
exports.postForm = function(req, res) {
// express form validation goes here.
if(errors) {
res.locals.errors = errors;
res.locals.form = req.body;
res.render('form_fail'); <----
}
else {
res.redirect('/welcome');
}
}
Related
In a node,js app using express.js, I have a piece of middleware that redirects to a session ended page written in nunjucks where need be.
The redirection works put the page is output as written and not as html. For some reason it's not being recognised as nunjucks.
nunjucks.configure(['views',
path.join(__dirname, 'node_modules/govuk-frontend/'),
path.join(__dirname, 'node_modules/govuk-frontend/govuk/components/'),
path.join(__dirname, 'app/views/')
], {
autoescape: true,
express: app
});
const endSession = (req, res, next) => {
if (config.switchPage) {
res.render(__dirname + '/app/views/pages/session-ended.html');
return;
} else {
next();
}
};
app.use(endSession);
Elsewhere, I call the page with:
res.redirect('/decision/session-ended');
and it works fine.
I've tried swapping the sendfile for the redirect but get the following error message on the page:
This page isn’t working
localhost redirected you too many times.
Try clearing your cookies.
ERR_TOO_MANY_REDIRECTS
==== ADDITIONAL INFO ====
This middleware gets kicked off here:
const getApplicationRef = (req, res) => {
req.session.accessPage = '/decision/application-reference';
res.render('pages/application-reference', { appRef: req.session.appRef });
};
which invokes:
const express = require('express');
const router = express.Router();
const { getApplicationRef, submitApplicationRef } = require('../../services/handler/application/application-ref-handler');
router.get('/', getApplicationRef);
router.post('/', submitApplicationRef);
module.exports = router;
getApplicationRef =
const getApplicationRef = (req, res) => {
req.session.accessPage = '/decision/application-reference';
res.render('pages/application-reference', { appRef: req.session.appRef });
};
And here's some code that renders the page properly:
const getSecurityCode = (req, res) => {
if (req.session.appRef) {
req.session.accessPage = '/decision/security-code';
res.render('pages/security-code', { secCode: req.session.secCode });
} else {
res.redirect('/decision/session-ended');
}
};
First off, the code you've shown in your question contains no nunjucks code at all so it's hard to see where you expect things to be using nunjucks. You aren't showing us very much specifics in your code so we can just offer the general advice.
If you want nunjucks to render the page, you need to call res.render("yourtemplate"), not res.sendFile(somefile) and you need to have properly registered nunjucks as your template engine.
If you're using res.redirect('/decision/session-ended');, then you need a route for /decision/session-ended that calls res.render() appropriately and does NOT do further redirecting and you need to make sure that route is not redirected by your middleware (probably just by placing that route handler before your middleware).
For more detail, please show the entire code flow that is relevant here from when the session ends to all requests involved in that.
I have a sign up form that I want to re-populate with the user entered data when the form is submitted but has errors in them. I am using express-validator and connect-flash to check / show error messages. I can't seem to figure out a way to pass the original values back to repopulate the field.
Here's my route:
router.post('/edit',
// Input validation
function(req, res, next) {
req.checkBody('username', 'Username cannot be empty').trim().notEmpty();
var errors = req.validationErrors(true);
if (errors) {
req.flash('validation', errors);
res.redirect('/vendor/edit/'));
} else {
//Add to DB
}
});
Here is where I either load the original form, or where it gets redirected to show the form with error messages. :
router.get('/edit', function(req, res) {
res.render('vendor_edit', {
validation: req.flash('validation')[0],
error: req.flash('error')[0],
});
});
However, the form is blank when it gets redirected since my template doesn't have the original values, or I don't know how to access them if they are naturally passed? - I am trying to render in PUG.
This is made possible via this post:
How do I redirect in expressjs while passing some context?
For the lazy, here is the copy and paste of the code from the above link, it worked like a charm for me.
var express = require('express');
var jade = require('jade');
var http = require("http");
var app = express();
var server = http.createServer(app);
/////////////
// Routing //
/////////////
// Move route middleware into named
// functions
function homeCtrl(req, res) {
// Prepare the context
var context = req.dataProcessed;
res.render('home.jade', context);
}
function categoryCtrl(req, res, next) {
// Process the data received in req.body
// instead of res.redirect('/');
req.dataProcessed = somethingYouDid;
return next();
// optionally - Same effect
// accept no need to define homeCtrl
// as the last piece of middleware
// return homeCtrl(req, res, next);
}
app.get('/', homeCtrl);
app.post('/category', categoryCtrl, homeCtrl);
I would like to send error messages back to the client without adding them to the url. Here is my attempt:
exports.register = function(req, res) {
if (req.body.password != req.body.password_repeat) {
res.locals.err = 'Passwords must match.';
res.locals.action = 'register';
res.redirect('/');
return;
}
...
exports.index = function(req, res) {
req.url = '/';
res.render('index', {
action: res.locals.action,
error: res.locals.error,
redirect: res.locals.redirect
});
};
So the redirect works fine and exports.index executes. The problem is that res.locals are gone by then. Is this because once I redirect it is considered a new req/res cycle? Any way I can pass this information through redirect without doing something like res.redirect('/?error=error')
You can use flash package from expressjs, but you need to have session middleware to use it. Also, you can use express-flash package from RGBboy but you need to have both cookieParser and session middlewares in this case.
I'm relatively new to web development, brand new to node.js.
I'm trying to use express to make a site that updates an api with the chef-node api client, using this tutorial
If I create a stand alone node.js app, it works as expected, and the value 'bacon' is set to 'good'
app1.js
var fs = require('fs'),
chef = require('chef'),
key = fs.readFileSync('/Users/foo.pem'),
chef_client = chef.createClient('foo', key, 'https://chef.example.com/organizations/dev');
var mybreakfast = { "id":"food","bacon":"good"}
chef_client.put('/data/breakfast/food', mybreakfast, function(err,res,body) {
if (err) { return console.log(err); }
console.log(body)
});
Full code is here.
The app basically looks like this:
app.js
var fs = require('fs'),
chef = require('chef'),
key = fs.readFileSync('/Users/foo.pem'),
chef_client = chef.createClient('foo', key, 'https://chef.example.com/organizations/dev');
...
//tutorial says this isn't ideal, but it is quickest way to get working
app.use(function(req,res,next){
req.client = chef_client;
next();
});
routes/index.js
var express = require('express');
var router = express.Router();
/* GET home page. */
router.get('/', function(req, res, next) {
res.render('index', { title: 'Express' });
});
router.get('/databags', function(req, res) {
req.client.get('/data/breakfast/food', function(err,res,body) {
if (err) { return console.log(err); }
console.log("Found breakfast")
console.log(body)
bag_data = body; //TODO: Global variable is bad and you should feel bad, how do I use correct variable?
});
res.render('databags.jade', { title: 'Databags', somedata: bag_data });
});
router.post('/databags', function(req, res) {
var mybreakfast = { "id":"food","bacon":"good"}
req.client.put('/data/breakfast/food', mybreakfast, function(err,res,body) {
if (err) { return console.log(err); }
console.log(body)
});
});
vies/databags.jade
html
body
form(action='/databags', id='derp', method='POST')
each value, key in somedata
label #{key}
input(type='text',name="#{key}", value="#{value}")
br
br
input(type='submit', value='Submit', form='derp')
When I press the submit button in /databags, I get a 301 and no data is uploaded.
<html>
<head><title>301 Moved Permanently</title></head>
<body bgcolor="white">
<center><h1>301 Moved Permanently</h1></center>
<hr><center>openresty/1.7.10.1</center>
</body>
</html>
I suspect the problem has to do with the fact that I'm adding client to every request.
//tutorial says this isn't ideal, but it is quickest way to get working
app.use(function(req,res,next){
req.client = chef_client;
next();
});
What is the proper way to make the chef_client variable in app.js available in routes/index.js?
If that is the correct way to do this, then what could be making the app work by itself, but not when used with the express framework?
Since this is my first node.js / express site, any other suggestions to get this app working would be very appreciated.
Update
Solution has been found, working code snippet available here: https://gist.github.com/spuder/1e39868b6a9a0c3cdb13
If route_index.js is your routes/index.js then your problem is that you're not actually exporting anything from it. When you require('routes/index') you're going to get back whatever you set module.exports to to in that file.
That means your routes/index.js file should end with:
module.exports = router;
To address your question regarding how to share chef_client without a global, you can return a factory from routes/index.js that returns an instantiated router using the parameters you pass to it. That would look something like this:
routes/chef.js
function addChefRoutes(router, chefClient) {
router.get('databags', function(res,req){
// ...
}
});
module.exports = addChefRoutes;
app.js
var chefClient = chef.createClient('foo', key, 'https://chef.example.com/organizations/dev');
var addChefRoutes = require('./routes/chef');
var router = express.Router();
addChefRoutes(router, chefClient);
So I'm starting to use Node.js. I saw the video with Ryan Dahl on Nodejs.org and heard he recommended Express-js for websites.
I downloaded the latest version of Express, and began to code. I have a fully fledged static view up on /, but as soon as I try sending parameters, I get errors like this:
Cannot GET /wiki
I tried following the guide on expressjs.com but the way one uses routes has changed in the latest version, which makes the guide unusable.
Guide:
app.get('/users/:id?', function(req, res, next){
var id = req.params.id;
if (id) {
// do something
} else {
next();
}
});
Generated by Express:
app.get('/', routes.index);
My problem arises when I try and add another route.
app.get('/wiki', routes.wiki_show);
I've tried a bunch of approaches, but I keep getting the Cannot GET /wiki (404) error.
routes/index.js looks like this:
exports.index = function(req, res) {
res.render('index', { title: 'Test', articles: articles, current_article: current_article, sections: sections })
};
The only thing I did there was add some parameters (arrays in the same file) and this i working. But when I copy the contents and change exports.index to exports.wiki or exports.wiki_show I still get the Cannot GET /wiki error.
Can anyone explain to me what I'm missing here? - Thanks.
So, after I created my question, I got this related list on the right with a similar issue: Organize routes in Node.js.
The answer in that post linked to the Express repo on GitHub and suggests to look at the 'route-separation' example.
This helped me change my code, and I now have it working. - Thanks for your comments.
My implementation ended up looking like this;
I require my routes in the app.js:
var express = require('express')
, site = require('./site')
, wiki = require('./wiki');
And I add my routes like this:
app.get('/', site.index);
app.get('/wiki/:id', wiki.show);
app.get('/wiki/:id/edit', wiki.edit);
I have two files called wiki.js and site.js in the root of my app, containing this:
exports.edit = function(req, res) {
var wiki_entry = req.params.id;
res.render('wiki/edit', {
title: 'Editing Wiki',
wiki: wiki_entry
})
}
The route-map express example matches url paths with objects which in turn matches http verbs with functions. This lays the routing out in a tree, which is concise and easy to read. The apps's entities are also written as objects with the functions as enclosed methods.
var express = require('../../lib/express')
, verbose = process.env.NODE_ENV != 'test'
, app = module.exports = express();
app.map = function(a, route){
route = route || '';
for (var key in a) {
switch (typeof a[key]) {
// { '/path': { ... }}
case 'object':
app.map(a[key], route + key);
break;
// get: function(){ ... }
case 'function':
if (verbose) console.log('%s %s', key, route);
app[key](route, a[key]);
break;
}
}
};
var users = {
list: function(req, res){
res.send('user list');
},
get: function(req, res){
res.send('user ' + req.params.uid);
},
del: function(req, res){
res.send('delete users');
}
};
var pets = {
list: function(req, res){
res.send('user ' + req.params.uid + '\'s pets');
},
del: function(req, res){
res.send('delete ' + req.params.uid + '\'s pet ' + req.params.pid);
}
};
app.map({
'/users': {
get: users.list,
del: users.del,
'/:uid': {
get: users.get,
'/pets': {
get: pets.list,
'/:pid': {
del: pets.del
}
}
}
}
});
app.listen(3000);
Seems that only index.js get loaded when you require("./routes") .
I used the following code in index.js to load the rest of the routes:
var fs = require('fs')
, path = require('path');
fs.readdirSync(__dirname).forEach(function(file){
var route_fname = __dirname + '/' + file;
var route_name = path.basename(route_fname, '.js');
if(route_name !== 'index' && route_name[0] !== "."){
exports[route_name] = require(route_fname)[route_name];
}
});
You could also organise them into modules. So it would be something like.
./
controllers
index.js
indexController.js
app.js
and then in the indexController.js of the controllers export your controllers.
//indexController.js
module.exports = function(){
//do some set up
var self = {
indexAction : function (req,res){
//do your thing
}
return self;
};
then in index.js of controllers dir
exports.indexController = require("./indexController");
and finally in app.js
var controllers = require("./controllers");
app.get("/",controllers.indexController().indexAction);
I think this approach allows for clearer seperation and also you can configure your controllers by passing perhaps a db connection in.
No one should ever have to keep writing app.use('/someRoute', require('someFile')) until it forms a heap of code.
It just doesn't make sense at all to be spending time invoking/defining routings. Even if you do need custom control, it's probably only for some of the time, and for the most bit you want to be able to just create a standard file structure of routings and have a module do it automatically.
Try Route Magic
As you scale your app, the routing invocations will start to form a giant heap of code that serves no purpose. You want to do just 2 lines of code to handle all the app.use routing invocations with Route Magic like this:
const magic = require('express-routemagic')
magic.use(app, __dirname, '[your route directory]')
For those you want to handle manually, just don't use pass the directory to Magic.