8

I recently followed a simple tutorial on how to build an Express server (https://codeforgeek.com/2014/06/express-nodejs-tutorial/).

I am trying to extend the code from this tutorial so that I can respond to post requests. I want to do this by updating a json file (that happens to be filled with 'user comments', and then rerendering at '/'

./server.js:

var express = require('express');
var app = express();

// routing configuration
require('./router/main')(app);

// ejs configuration
app.set('views', __dirname + '/views');
app.set('view engine', 'ejs');
app.engine('html', require('ejs').renderFile);

// run the server
var server = app.listen(8080, function(){
  console.log('Express server listening on port 8080');
});

./router/main.js (routers):

var fs = require('fs');
var ejs = require('ejs')

module.exports = function(app){

  app.get('/', function(req, res){
    var comments = JSON.parse(fs.readFileSync(__dirname + '/../comments.json'));
    res.render('index.ejs', comments);
  });

  app.post('/', function(req, res){
    console.log('here in post');
    var name = req.body.name;
    var message = req.body.message;
    var newComment = {"name": name, "message": message};
    var comments = JSON.parse(fs.readFileSync(__dirname + '/../comments.json'));
    comments.push(newComment);
    fs.writeFileSync(__dirname + '/../comments.json', comments, 'utf8');
    //redirect to a 'get' on '/'
    res.redirect('/');
  });

  app.get('/about', function(req, res){
    res.render('about.html')
  });

}

./views/index.ejs:

<div>

  <div>
    <h1> Joe's Forum </h1>
    <a href='/about'> (about) </a>
  </div>

  <div>
    <ul>
    <% comments.forEach( function(comment){ %>
      <li>
        <%= comment.name %> : <%= comment.message %>
      </li>
    <% }); %>
    </ul>
  </div>

  <h2> Enter a new comment </h2>

  <form action='/' method="post">
    Enter your name: <input type='text' name='name'> <br><br>
    Enter your message: <input type='textarea' name='message'> <br><br>
    <input type='submit' value='Submit'>
  <form>

</div>

./comments.json:

{
  "comments": [
    {"name":"Joe", "message" : "What advantages does Node.js afford the web developer?"},
    {"name": "John", "message": "Asynchronous IO helps us to keep our pages responsive even if the server is fetching data"}
  ]
}

When I try to submit a new comment from my form, all I see is this:

"Cannot POST /"

Can someone please explain why I might be getting this error? Thanks

Joseph Palermo
  • 327
  • 2
  • 3
  • 12
  • You're doing something of real no-no. You should never ever use the `Sync` api of nodeJS and certainly not in the response from an express server. There's also no error checking anywhere, so your server will be very crash prone. Can you show the contents of `comments.json`? – caasjj Sep 23 '15 at 16:43
  • Ok fair enough. However, I just wanted to 'make it work' as an exercise in leaning Express. I know it isn't great code. Once I get the 'post request' working, I intend to clean it up (i.e. subscribe to best practices). As requested I'll edit to include comments.json. – Joseph Palermo Sep 23 '15 at 17:46
  • Wasn't at all meant as criticism - just pointing out something you might or might not be aware of. Now, I think I also understand why your code is misbehaving. You're doing `comments.push` on an `Object` - rather than an `Array`. Let me see if I can hack up a clear answer for you. – caasjj Sep 23 '15 at 17:51
  • Thanks for the advice, I do appreciate it. Also, thanks for pointing out that error with comments. Although I do think there's more to the problem than just that. – Joseph Palermo Sep 23 '15 at 18:06
  • The real problem is you're using `req.body` without a module to parse the body. Please see solution below. – caasjj Sep 23 '15 at 18:36

1 Answers1

10

There are actually a couple of problems, but the main one is that you don't have a body parser - the module that converts a node stream in the POST to a req.body. I am currently only familiar with bodyParser, and you should probably research that a bit. Although it is shown in Express 4.x documentation, you get a deprecation message when you run the server.

The other problem is the issue of comments.push. That should be comments.comments.push. The following works:

router.js:

var fs = require('fs');
var ejs = require('ejs')

module.exports = function(app){

  app.get('/', function(req, res){
    var comments = JSON.parse(fs.readFileSync(__dirname + '/../comments.json'));
    res.render('index.ejs', comments);
  });

  app.post('/', function(req, res){
    console.log('here in post');
    console.log(req.body)
    var name = req.body.name;
    var message = req.body.message;
    var newComment = {"name": name, "message": message};
    var comments = JSON.parse(fs.readFileSync(__dirname + '/../comments.json'));
    comments.comments.push(newComment);
    fs.writeFileSync(__dirname + '/../comments.json', JSON.stringify(comments), 'utf8');
    //redirect to a 'get' on '/'
    res.redirect('/');
  });

  app.get('/about', function(req, res){
    res.render('about.html')
  });

}

and server.js:

var express = require('express');
var bodyParser = require('body-parser');
var app = express();

app.use(bodyParser.urlencoded())

// routing configuration
require('./router/main')(app);

// ejs configuration
app.set('views', __dirname + '/views');
app.set('view engine', 'ejs');
app.engine('html', require('ejs').renderFile);

// run the server
var server = app.listen(8080, function(){
  console.log('Express server listening on port 8080');
})
caasjj
  • 1,354
  • 8
  • 11
  • Appreciate that! Please do look into the issue of body parsing and middleware in general, as that will prove very instructive to how Express works. – caasjj Sep 23 '15 at 18:41