はじめに
ドットインストールのExpress講座は最終更新日が2014年のままアーカイブされていて、講座内容そのままやってもうまくいかない箇所がある。
「» 19 記事を更新/削除してみよう」
の章では、Expressのmethod-overrideモジュールの仕様が変わっているため、POSTメソッドの書き換えができず、エラーが出てしまった。新しい仕様に対応した正しいやり方をメモしておく。
Expressの公式ドキュメントでは、Express3からExpress4にバージョンが変わって以降、仕様変更されたミドルウェア・システムがまとめられている。
「Express 4 への移行」
https://expressjs.com/ja/guide/migrating-4.html
※「method-overrideモジュール」とは
ブラウザを、HTTPのPUT,DELETEメソッドに対応させるためのモジュール。
Express では get、post、put、delete というHTTPリクエストを送れるが、ブラウザは get と post にしか対応していない。
「method-overrideモジュール」を読み込むと、ブラウザを put と delete に対応させることができる。
ドットインストールで作成するブログアプリのファイル構成
├── 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
varexpress=require('express'),app=express(),post=require('./routes/post');app.set('views',__dirname+'/views');app.set('view engine','ejs');// middlewareapp.use(express.json());app.use(express.urlencoded());app.use(express.methodOverride()); //Express3仕様の書き方(エラーの原因)app.use(express.logger('dev'));app.use(app.router);// routingapp.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【編集ページ】
<%-include('../partials/header');%><h1>Edit</h1>
//HTMLにはputメソッドがないため、postメソッドをサーバー側で変更する<formmethod="post"action="/posts/<%= id %>"><inputtype="text"name="title"value="<%= post.title %>"><inputtype="text"name="body"value="<%= post.body %>">//以下の一文を入れるとputメソッドとして認識されるはず(実際にはPOSTメソッドと認識されエラーに)<inputtype="hidden"name="_method"value="put"><inputtype="hidden"name="id"value="<%= id %>"><inputtype="submit"value="更新"></form>
<p><ahref="/">BacktoTop</a></p><%-include('../partials/footer');%>
・index.ejs【記事タイトル一覧&編集・削除ボタン】
<%-include('../partials/header');%><h1>Posts</h1>
<ul><%for(vari=0;i<posts.length;i++){%><li><ahref="/posts/<%= i %>"><%=posts[i].title%></a>
<ahref="/posts/<%= i %>/edit">[Edit]</a>
//HTMLにはdeleteメソッドがないため、見せかけのコードで代用<formmethod="post"action="/posts/<%= i %>">//以下の一文を入れるとdeleteメソッドとして認識されるはず(実際にはPOSTメソッドと認識されエラーに)<inputtype="hidden"name="_method"value="delete"><inputtype="hidden"name="id"value="<%= i %>"><inputtype="submit"value="削除"></form>
</li>
<%}%></ul>
<p><ahref="/posts/new">Addnew</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(サーバーサイド)
varexpress=require('express')varmethodOverride=require('method-override')varapp=express()// override with POST having ?_method=DELETEapp.use(methodOverride('_method'))
・HTML(フロントエンド)
<formmethod="POST"action="/resource?_method=DELETE"><buttontype="submit">Delete resource</button></form>
Express4バージョンで実装
・app.js
constexpress=require('express');constapp=express();//POSTで渡ってきたものをreq.resで取得するmiddlewareのためのモジュール (※bodyParserはExpress4では不要?未確認)constbodyParser=require('body-parser');constmethodOverride=require('method-override');//ルーティングを記述したファイルをモジュールとして読み込みconstpost=require('./routes/post')app.set('views',__dirname+'/views');app.set('view engine','ejs');//POSTで渡ってきたものをreq.resで取得するためのmiddlewareapp.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【編集ページ】
<%-include('../partials/header');%><h1>Edit</h1>
//クエリ値を指定してHTTPメソッドを上書き<formmethod="post"action="/posts/<%= id %>?_method=PUT"><inputtype="text"name="title"value="<%= post.title %>"><inputtype="text"name="body"value="<%= post.body %>"><inputtype="hidden"name="id"value="<%= id %>"><inputtype="submit"value="Update!"></form>
<p><ahref="/">BacktoTop</a></p><%-include('../partials/footer');%>
・index.ejs【記事タイトル一覧&編集・削除ボタン】
<%-include('../partials/header');%><h1>Posts</h1>
<ul><%for(vari=0;i<posts.length;i++){%><li><ahref="/posts/<%= i %>"><%=posts[i].title%></a>
<ahref="/posts/<%= i %>/edit">[Edit]</a>
//クエリ値を指定してHTTPメソッドを上書き<formmethod="post"action="/posts/<%= i %>?_method=DELETE"><inputtype="hidden"name="id"value="<%= i %>"><inputtype="submit"value="削除"></form>
</li>
<%}%></ul>
<p><ahref="/posts/new">Addnew</a></p><%-include('../partials/footer');%>