Expressでフォーム送信/受信

Node.js+Expressを使ってフォーム送信/受信するページを作りたいと思います。
https://qiita.com/watsuyo_2/items/327b74d68f921dbdd524を参考にしました。

まずは http://localhost:3000/hello でページが表示されるように routes の設定をします。

app.js の設定

App.js に2行のコードを追記します。

var helloRouter = require('./routes/hello');
app.use('/hello', helloRouter);

routes の設定

routes ディレクトリに hello.js を作ります。
ここは参考ページから丸々コピペしました。

const express = require('express')
const router = express.Router()

// define the home page route
router.get('/', function (req, res, next) {
  const data = {
    title: '会員登録情報の確認',
    content: '会員確認のため、お名前をご記入ください'
  }
  res.render('hello', data);
  res.send('Birds home page')
})

// define the about route
router.get('/about', function (req, res) {
  res.send('About birds')
})

router.post('/confirm', (req, res, next) => {
  const name = req.body['userName'];
  const data = {
    title: '登録情報の確認完了!',
    content: name + 'さんの会員登録情報が確認できました。'
  }
  res.render('hello', data);
})

module.exports = router;

しかし、このままブラウザで表示すると「Error: Cannot find module ‘html’」と出てエラーになってしまいます。
モジュールHTMLが見つからないよと怒られてしまいました。

そこで、https://www.code-adviser.com/detail_26779846 を参考にして app.js に以下のコードを追加しました。

app.use(express.static(__dirname + '/public'));
app.set('views', __dirname + '/public/views');
app.engine('html', require('ejs').renderFile);
app.set('view engine', 'html');

今度はCannot find module ‘ejs’と怒られたので ejs モジュールをインストールします。

$ npm install --save ejs

ここでブラウザを表示します。

$node app.js

1度ブラウザに正しく表示されるようになりましたが、すぐにターミナルにエラーが吐き出されて中断されました。

$ node app.js
Example app listening on port 3000!
_http_outgoing.js:467
    throw new ERR_HTTP_HEADERS_SENT('set');
    ^

Error [ERR_HTTP_HEADERS_SENT]: Cannot set headers after they are sent to the client
    at ServerResponse.setHeader (_http_outgoing.js:467:11)

なんだなんだ?
送信した後にヘッダー設定しているぞ!ということですかね。

res.send() が res.render() より先に読み込むように順番を変更して再度実行してみました。
正常に動きました!!!

HTMLファイルを作成

続いて hello.html を作ります。
参考ページではpugを使っていますが、ここでは普通にHTMLとして使いたいです。

Ejsは既にインストールして app.js にいろいろ設定しているのでHTMLが使えるはずです。
受け取った data を表示するにはどうしたら良いのでしょうか?

良い参考ページを見つけました。
https://qiita.com/kamihork/items/1b13d2157979d1837849 を参考に作りました。

<!DOCTYPE html>
<html lang="ja">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <meta http-equiv="X-UA-Compatible" content="ie=edge">
  <title><%- title %></title>
</head>
<body>
  <h1><%- title %></h1>
  <p><%- content %></p>
</body>
</html>

このHTMLファイルを /public/views に置いて app.js を実行します。

$ node app.js

このように hello.js のデータが渡されてブラウザ上で表示することができました!
あとはここにフォームを追加するだけです。

フォームを追加する

フォームを追加して送信してみました。

URLがちゃんと「http://localhost:3000/hello/confirm」に変わったけれどエラー…。

TypeError: Cannot read property ‘userName’ of undefined

こんなときはGoogle先生!
検索して良いものを見つけました。
https://blog.ryo4004.net/web/306/

//app.js に下記を追記。
const bodyParser = require('body-parser')
app.use(bodyParser.urlencoded({extended: true}))
app.use(bodyParser.json())

ちゃんと動きました!
npm でbody-parserをインストールする必要があると書いてありますが、既にインストールされているので特にしませんでした。

コードの最終形態

//app.js
const express = require('express')
const app = express()

app.use(express.static(__dirname + '/public'));
app.set('views', __dirname + '/public/views');
app.engine('html', require('ejs').renderFile);
app.set('view engine', 'html');

const bodyParser = require('body-parser')
app.use(bodyParser.urlencoded({extended: true}))
app.use(bodyParser.json())

const helloRouter = require('./routes/hello')
app.use('/hello', helloRouter);

app.get('/', (req, res) => res.send('HelloWorld'))

app.listen(3000, () => console.log('Example app listening on port 3000!'))
// hello.js
const express = require('express')
const router = express.Router()

// define the home page route
router.get('/', function (req, res, next) {
  const data = {
    title: '会員登録情報の確認',
    content: '会員確認のため、お名前をご記入ください'
  }
  res.render('hello', data);
})

router.post('/confirm', (req, res, next) => {
  const name = req.body['userName'];
  const data = {
    title: '登録情報の確認完了!',
    content: name + 'さんの会員登録情報が確認できました。'
  }
  res.render('hello', data);
})

module.exports = router;
//hello.html
<!DOCTYPE html>
<html lang="ja">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <meta http-equiv="X-UA-Compatible" content="ie=edge">
  <title><%- title %></title>
</head>
<body>
  <h1><%- title %></h1>
  <p><%- content %></p>
  <form action="hello/confirm" method="post">
    <input type="text" name="userName" value="">
    <input type="submit" name="送信">
  </form>
</body>
</html>

フォローお願いします