きっかけ
書籍のwebを支える技術を読んでいたら、p98に以下の記述がありました。
しかし、現実に一番よく利用されているのはGETとPOSTの2つです。
これはHTMLのフォームで指定できるメソッドがGETとPOSTだけという制限に起因します。
これを読んで、「だとすると、updateアクション(PATCHメソッド)はどうやって実現されるのだろう??」
と思ったのですが、すぐ隣のページに書かれてました。
どうやらRailsは_method
パラメーターを使って実現させているみたいです。
実際に確認してみる
お知らせを投稿できるNoticeというModelがあるとします。
###ルーティング
(当たり前ですが)updateアクションはPATCH
かPUT
で発動となっています。
$ rails routes|grep notice
notices GET /notices(.:format) notices#index
POST /notices(.:format) notices#create
new_notice GET /notices/new(.:format) notices#new
edit_notice GET /notices/:id/edit(.:format) notices#edit
notice PATCH /notices/:id(.:format) notices#update
PUT /notices/:id(.:format) notices#update
DELETE /notices/:id(.:format) notices#destroy
HTML
editページのformを右クリック→ページのソースを表示
で見てみます。
<form class="edit_notice" id="edit_notice_18" action="/notices/18" accept-charset="UTF-8" method="post"><input name="utf8" type="hidden" value="✓" />
<input type="hidden" name="_method" value="patch" /><input type="hidden" name="authenticity_token" value="lGc0ypr/fV1WxhHZm6OS1ehZMpPj9IIgKF/8S5pm3FglyNHWxFgGLGt25AjoFb9BAINs4iRwg7VXn8eLjv9QGQ==" />
<label>お知らせ</label>
<textarea name="notice[body]" id="notice_body">test</textarea>
<input type="submit" name="commit" value="登録" data-disable-with="登録中…" />
</form>
確かにformタグを見ると、 method="put" や method="patch"ではなく、 method="post"
となっています。
そして、隠しパラメータ(hidden)に_method
パラメータを用意しているのが確認できます。↓
<input type="hidden" name="_method" value="patch" />
Wiresharkでパケットキャプチャーして見た
リアルなhttpパケットが見たいのでキャプってみました。
wiresharkでインターフェイスをlocalhostに指定してあげると、localhost:3000とのパケット通信をキャプチャーできます。
画像はeditページでsubmitした時のhttpパケットです。
確かに**POST
メソッドのHTTPリクエストが送信されている**ことが確認できます。
また、HTML Form URL Encoded: application/x-www-form-urlencoded
内で
Form item: "_method" = "patch"
というパラメーターが確認できる。
おそらくこのパラメーターを見て**"patch"
メソッドが送られたこととして処理している**んだと思われる(本当はPOSTメソッドを受け取っているが)。
デバッグ
updateアクション内にbinding.pryを仕込んでデバッグ。
[1] pry(#<NoticesController>)> request
=> #<ActionDispatch::Request:0x00007fd6cb6156d0
@env=
{"CONTENT_LENGTH"=>"210",
"CONTENT_TYPE"=>"application/x-www-form-urlencoded",
"GATEWAY_INTERFACE"=>"CGI/1.1",
"PATH_INFO"=>"/notices/15",
"QUERY_STRING"=>"",
"REMOTE_ADDR"=>"::1",
"REMOTE_HOST"=>"::1",
"REQUEST_METHOD"=>"PATCH",
"REQUEST_URI"=>"http://localhost:3000/notices/15",
"SCRIPT_NAME"=>"",
"SERVER_NAME"=>"localhost",
"SERVER_PORT"=>"3000",
実際に送信したHTTPリクエストはPOSTメソッドでしたが、Rails上ではPATCHメソッドを受け取ったと認識して処理しているようです。
("REQUEST_METHOD"=>"PATCH"
)
#疑問に思ったこと
今回の確認では_methodでpatchが当てがわれているが、putはどのような条件で出てくるのだろう??
(何かの条件でputのパターン、patchのパターンがあるのだろうか)