かもメモ

自分の落ちた落とし穴に何度も落ちる人のメモ帳

Express 4.x でmethod-overrideでput / delete メソッドを使いたいにハマる。

ドットインストールのExpress入門 をやっていて、サンプルはExpress3系なのですが、4系でやっていたので色々ハマりました。
たいていはモジュールを別途インストールして〜で済んだのですが、
#12 ルーティングを確認しようPUTDELETEメソッドに対応させるためにapp.use( express.methodOverride() )というミドルウェアを使うとありました。
Express4系では、別途method-overrideを使うのだとわかったので

// app.js
var methodOverride = require('method-override');

app.use( methodOverride() );

として、フォームには

// edit.ejs
<form method="post" action="/posts/<%= id %>">
 // 中略
  <input type="hidden" name="_method" value="put">
  <input type="submit" value="update">
</form>

のような_methodを追加したのですが、フォームを送信してもPUT・DELETEではなく、フォームのactionで指定しているPOSTメソッドとして解釈されてしまいました。
 

1. _method の付け方を変更する方法

override using a query value

To use a query string value to override the method, specify the query string key as a string argument to the methodOverride function. To then make the call, send a POST request to a URL with the overridden method as the value of that query string key. This method of using a query value would typically be used in conjunction with plain HTML
elements when trying to support legacy browsers but still use newer methods.

override using a query value | expressjs/method-override - GitHub

どうやら、methodOverride()メソッドを書き換えるためのキーを指定して、input[type="hidden"]で_methodを渡すのではなくフォームのアクションに?_method=としてあげると良いようです。フォームのmethodがGETならinput[type="hidden"]でも上手くいくのかな???

GitHubのサンプルコードではrequire('connect')していたので、これをインストールして、app.jsとedit.ejsを書き換えます。

$ npm install --save connect
// app.js
var connect        = require('connect');
var methodOverride = require('method-override');

app.use( methodOverride('_method') ); // キーを指定
// edit.ejs
<form method="post" action="/posts/<%= id %>?_method=PUT">
 // 中略
  <input type="submit" value="update">
</form>

DELETEメソッドも同様にaction="/posts/<%= id %>?_method=DELETE"にすれば、PUT・DELETEメソッドとして解釈されるようになりました。
 

2. ?method= ではなく input[type="hidden"]でmethodを渡す方法

POSTのフォームなのにURLに?_methodってパラメーターが付いてるのがなんだか少しかっちょ悪い気がします... GitHubのページを見ていると、input[type="hidden"]で_methodを渡す方法も書かれていました。

custom logic

You can implement any kind of custom logic with a function for the getter. The following implements the logic for looking in req.body that was in method-override@1:

custom logic | expressjs/method-override - GitHub

req.body内に_methodパラメーターがあるか探してメソッドを書き換える処理を自前で書けば、ドットインストールのサンプルのようにフォーム内にinput[type="hidden", name="_method", value="put / delete"]でPUT・DELETEメソッドが使えるようになるようです。

フォームの有るedit.ejsは最初の状態のままでOKなので、app.jsを修正します。

// app.js
var bodyParser     = require('body-parser');
var connect        = require('connect');
var methodOverride = require('method-override');

// ↓ これはドットインストールのサンプルで4系用に作ってたミドルウェア
app.use( bodyParser.urlencoded() );

// method の変更処理を自前で書く方法にする
app.use( methodOverride(function(req, res){
  if (req.body && typeof req.body === 'object' && '_method' in req.body) {
    // look in urlencoded POST bodies and delete it
    var method = req.body._method;
    delete req.body._method;
    return method;
  }
}) );

この方法で、ドットインストールのサンプルと同じ様にフォームの中にinputタグで_methodを書いておく方法でPUT・DELETEメソッドが使えるようになりました。
 

感想

Expressはなんとなく使ってたので、理解できてない部分が多かったので、最初からバージョンを合わせてレッスンするべきだったかもしれませんが、ハマって自分で調べることでより理解が進むような気もしているのでハマって良かったかなぁーと思うことにします。
method-overrideでPUT・DELETEが使える、いわゆるREST(って言うのですかね?)にする方法がけっこう大変なので、もしかしたらもっと良いExpress4系のモジュールとかが有るるのでしょうか?気になります。
 


[参考]

Webを支える技術 -HTTP、URI、HTML、そしてREST (WEB+DB PRESS plus)

Webを支える技術 -HTTP、URI、HTML、そしてREST (WEB+DB PRESS plus)