form actionでPUTとDELETEが送れない件

テンプレートエンジン ejs を用いて管理認証アプリを作っていました。 Log Out ボタンで DELETE リクエストを送ろうとしたところ...

index.ejs
<h1>Hello <%= name %> </h1> <form action="/logout" method="DELETE"> <button type="submit">Log Out</button> </form>

画面に表示される、Cannot GET /logout

どうやら HTML の form では送れないらしい

Method override for PUT and DELETE in HTML external_linkより引用

In both HTML4 and HTML5 spec, it says that the only HTTP methods that HTML form element should allow are "GET" and "POST".

HTML の仕様で GET・POST しか許されていないそうです。 なぜなのか、仕様をもっと探るために原典を調べてみます。

Web技術の標準化を行う W3C1 という非営利団体が存在しているようです。
その団体が仕様標準化の最終ステップとして公開する 「W3C勧告」(Last Updated 20 March 2020) external_linkにアクセスしてみます。

Form submissions are exposed to servers in a variety of ways, most commonly as HTTP GET or POST requests.

W3C勧告 では PUT・DELETE をサポートしていない理由が見つかりませんでした。(PUT・DELETEの文字すら書いてないような...?)

why html don't supported put request で検索

英語で調べてみるとそれっぽい答えが見つかりました。
Why don't browsers support PUT and DELETE requests and when will they? external_link で Nicholas Shanks さんが次のように回答しています。

There was much discussion about this during the development of HTML 5, and at one point they got added to HTML 5, only to be removed again. The reason the additional methods were removed from the HTML 5 spec is because HTML 4-level browsers could never support them (not being part of HTML at the time they were made); and there is no way to allow them to do so without a JavaScript shim; thus, you may as well use AJAX.

HTML4 レベルのブラウザーが新しいメソッドとして追加される PUT・DELETE をサポートできなかったためだそう。
そのため HTML5 開発中の議論の中で淘汰されてしまった...

結論どうやってリクエストを送るの?

上記のサイトでも解決策として挙げられていましたが、ヘッダーを使用してメソッドをオーバーライドすることで対応できます。

method-override external_link というライブラリをインストールします。 そしてForm を次のように変更、

index.ejs
<h1>Hello <%= name %> </h1> <form action="/logout?_method=DELETE" method="POST"> <button type="submit">Log Out</button> </form>

server.js にて次の2行を追加

server.js
const methodOverride = require('method-override') app.use(methodOverride('_method'))

image.png

想定した 302 コードが返ってきているので成功🎉

ちなみに GROWI では

route/index.js
app.post('/_api/likes.add', accessTokenParser, loginRequiredStrictly, csrf, page.api.like); app.post('/_api/likes.remove', accessTokenParser, loginRequiredStrictly, csrf, page.api.unlike);

かなり力技でした。NotRESTfull

Footnotes

  1. 「World Wide Web Consortium」の略称
    W3Cとは?Web標準化の重要性とW3Cの勧告プロセス external_link