【ドットインストール】Express入門講座の仕様変更点【 #19 記事を更新/削除してみよう】

##はじめに

ドットインストールのExpress講座は最終更新日が２０１４年のままアーカイブされていて、講座内容そのままやってもうまくいかない箇所がある。

「» 19 記事を更新/削除してみよう」
の章では、Expressのmethod-overrideモジュールの仕様が変わっているため、POSTメソッドの書き換えができず、エラーが出てしまった。新しい仕様に対応した正しいやり方をメモしておく。

Expressの公式ドキュメントでは、Express3からExpress4にバージョンが変わって以降、仕様変更されたミドルウェア・システムがまとめられている。

「Express 4 への移行」
https://expressjs.com/ja/guide/migrating-4.html

<h5>※「method-overrideモジュール」とは</h5>
<h6>ブラウザを、HTTPのPUT,DELETEメソッドに対応させるためのモジュール。</h6>
Express では get、post、put、delete というHTTPリクエストを送れるが、ブラウザは get と post にしか対応していない。
「method-overrideモジュール」を読み込むと、ブラウザを put と delete に対応させることができる。

##ドットインストールで作成するブログアプリのファイル構成

```bash
├── app.js
├── node_modules
├── package-lock.json
├── package.json
├── routes   　　　　　　　　//ルーティング処理
│   └── post.js　
└── views   　　　　　　　　//テンプレートエンジン
    ├── partials　　　　　//UIの共通パーツを別ファイル化
    │   ├── footer.ejs
    │   └── header.ejs
    └── posts
        ├── edit.ejs 　　//編集ページ
        ├── index.ejs 　//記事タイトル一覧ページ（編集・削除ボタン付き）
        ├── new.ejs
        └── show.ejs
```


##講座のままだとエラーになる箇所

・app.js

```javascript
var express = require('express'),
    app = express(),
    post = require('./routes/post');

app.set('views', __dirname + '/views');
app.set('view engine', 'ejs');

// middleware
app.use(express.json());
app.use(express.urlencoded());
app.use(express.methodOverride());　//Express3仕様の書き方（エラーの原因）

app.use(express.logger('dev'));
app.use(app.router);

// routing

app.get('/', post.index);
app.get('/posts/:id([0-9]+)', post.show);
app.get('/posts/new', post.new);
app.post('/posts/create', post.create);
app.get('/posts/:id/edit', post.edit);
app.put('/posts/:id', post.update);
app.delete('/posts/:id', post.destroy);

app.listen(3000);
console.log("server starting...");

```

・edit.ejs【編集ページ】

```javascript
<%- include ('../partials/header'); %>
<h1>Edit</h1>
//HTMLにはputメソッドがないため、postメソッドをサーバー側で変更する
<form method="post" action="/posts/<%= id %>">
   <input type="text" name="title" value="<%= post.title %>">
   <input type="text" name="body" value="<%= post.body %>">

//以下の一文を入れるとputメソッドとして認識されるはず（実際にはPOSTメソッドと認識されエラーに）
   <input type="hidden" name="_method" value="put">

   <input type="hidden" name="id" value="<%= id %>">
   <input type="submit" value="更新">
</form>

<p><a href="/">Back to Top</a></p>
<%- include ('../partials/footer'); %>
```



・index.ejs【記事タイトル一覧＆編集・削除ボタン】 

```javascript
<%- include ('../partials/header'); %>
<h1>Posts</h1>
<ul>
<% for (var i=0; i<posts.length; i++){ %>
<li>
    <a href="/posts/<%= i %>"><%= posts[i].title %></a>
    <a href="/posts/<%= i %>/edit">[Edit]</a>

//HTMLにはdeleteメソッドがないため、見せかけのコードで代用
    <form method="post" action="/posts/<%= i %>">
//以下の一文を入れるとdeleteメソッドとして認識されるはず（実際にはPOSTメソッドと認識されエラーに）
        <input type="hidden" name="_method" value="delete">

        <input type="hidden" name="id" value="<%= i %>">
        <input type="submit" value="削除">
    </form>
</li>
<% } %>
</ul>
<p><a href="/posts/new">Add new</a></p>
<%- include ('../partials/footer'); %>
```
##method overrideの変更点

『Express公式ドキュメント　method-override』( http://expressjs.com/en/resources/middleware/method-override.html )

「クエリ値を使用して上書きする(override using a query value)」
を参照

>クエリ文字列の値を使用してメソッドをオーバーライドするには、methodOverride 関数の文字列引数にクエリ文字列キーを指定します。呼び出しを行うには、そのクエリ文字列キーの値としてオーバーライドされたメソッドを持つ URL に POST リクエストを送信します。クエリ値を使用するこの方法は、レガシーなブラウザをサポートしつつも新しいメソッドを使用しようとしている場合、通常はプレーンなHTML \<form>要素と一緒に使用されます。

・javascript(サーバーサイド)

```javascript

var express = require('express')
var methodOverride = require('method-override')
var app = express()

// override with POST having ?_method=DELETE
app.use(methodOverride('_method'))
```

・HTML（フロントエンド）

```HTML
<form method="POST" action="/resource?_method=DELETE">
  <button type="submit">Delete resource</button>
</form>
```

##Express4バージョンで実装
・app.js

```javascript
const express = require('express');
const app = express();
//POSTで渡ってきたものをreq.resで取得するmiddlewareのためのモジュール　(※bodyParserはExpress4では不要？未確認)
const bodyParser = require('body-parser');
const methodOverride = require('method-override');
//ルーティングを記述したファイルをモジュールとして読み込み
const post = require('./routes/post')

app.set('views', __dirname + '/views');
app.set('view engine', 'ejs');

//POSTで渡ってきたものをreq.resで取得するためのmiddleware
app.use(bodyParser.json())
app.use(bodyParser.urlencoded({extended:false}));
//methodoverrideの引値を設定
app.use(methodOverride('_method'))

//ルーティング
app.get('/',post.index);
app.get('/posts/:id([0-9]+)',post.show);
app.get('/posts/new',post.new);
app.post('/posts/create',parseForm, csrfProtection,post.create);
app.get('/posts/:id/edit',csrfProtection, post.edit);
app.put('/posts/:id/',post.update);
app.delete('/posts/:id/',post.destroy);


app.listen(3000, () => {
  console.log('server is up on port 3000')
});


```

・edit.ejs【編集ページ】

```javascript
<%- include ('../partials/header'); %>
<h1>Edit</h1>
//クエリ値を指定してHTTPメソッドを上書き
<form method="post" action="/posts/<%= id %>?_method=PUT">
<input type="text" name="title" value="<%= post.title %>">
<input type="text" name="body" value="<%= post.body %>">
<input type="hidden" name="id" value="<%= id %>">

<input type="submit" value="Update!">
</form>

<p><a href="/">Back to Top</a></p>
<%- include ('../partials/footer'); %>
```

・index.ejs【記事タイトル一覧＆編集・削除ボタン】 

```javascript
<%- include ('../partials/header'); %>
<h1>Posts</h1>
<ul>
<% for (var i=0; i<posts.length; i++){ %>
<li>
    <a href="/posts/<%= i %>"><%= posts[i].title %></a>
    <a href="/posts/<%= i %>/edit">[Edit]</a>
//クエリ値を指定してHTTPメソッドを上書き
    <form method="post" action="/posts/<%= i %>?_method=DELETE">
        <input type="hidden" name="id" value="<%= i %>">
        <input type="submit" value="削除">
    </form>
</li>
<% } %>
</ul>
<p><a href="/posts/new">Add new</a></p>
<%- include ('../partials/footer'); %>
```