created_at
updated_at
tags
toc

なぜ HTML の form は PUT / DELETE をサポートしないのか?

Intro

10 年ほど前に同じことを調べたことがある。

当時は全くの素人で、素人なりに調査はしたが、ほとんどが推測の域を出ない結論だった。

この問題についてあらためて記す。

仕様策定の経緯

表題の通り、<form>method には GETPOST しかサポートされていない。HTTP には他にも PUTDELETE といったメソッドもあるのに、なぜサポートされていないのかという疑問から始まった。

仕様が決定した経緯は、以下に残っている。

Status: Rejected

Change Description: no spec change

Rationale: PUT as a form method makes no sense, you wouldn't want to PUT a form payload.

DELETE only makes sense if there is no payload, so it doesn't make much sense with forms either.

https://www.w3.org/Bugs/Public/show_bug.cgi?id=10671#c16

今思えば、そもそもここで説明されている通りだったが、当時はこの Rationale が説明不足に思え、腑に落ちていなかったんだと思う。

実は数年前、この件について直接 Hixie に聞いてみたことがある。

Hixie (Ian Hickson) は、WHATWG のスペックエディターとして HTML や CSS の仕様を多く作成した人物だ。いわゆる HTML5 系の API を強力に推進し、ブログが使う Pingback の仕様や、Server Sent Events / WebSocket といった野心的な仕様、画像への alt についてのレポートを何本も残したりしている。Netscape, Opera, Google と渡り歩き、今の Web の基礎を作った人物の一人と言って差し支えないだろう。筆者にとってレジェンドの一人だ。

もう数年前に WHATWG を離れてはいるが、彼は筆者の質問に丁寧に答えてくれた。彼とのやりとりは非常にエキサイティングだった。その記憶を頼りに書いておく。

Form と HTTP

そもそも、なぜそれを <form> に期待するのかの方に考察の余地がある、というものだ。

PUT はざっくり言えば「この URL にこのペイロードを置く(PUT)する」というものだ。しかし、<form> でできるのは application/x-www-form-urlencodedmultipart/form-data だけであり、こうしたペイロードをそのままサーバに置き(PUT)たいユースケースはない。ファイルアップロードだとしても、置きたいのは結合されたデータであって Multipart ではない。サーバで復元するのであれば、POST とやっていることが変わらない。

一方 DELETE は「この URL のリソースを削除する」という用途であり、そこにペイロードは不要だ。<form> はそもそもペイロードを送るためのものであるため、その時点で齟齬がある。仮に GET のようにクエリを <input> で付与し DELETE リクエストしたとしても、DELETE は基本的にレスポンス Body を想定しない。しかし、<form> はブラウザにとって Navigation のトリガーであるため、202 や 204 のようなレスポンスに対し、「ブラウザは何をすればいいのか」という問題になる。ペイロードを送って結果画面を受け取るなら、それは POST とやっていることが変わらない。

結局、全て POST のユースケースでカバーできているのだ。

「でも〜」となぜ言いたくなるのか

最初の Hixie のスレッドには、「でも〜」というレスポンスが非常に多くある。同様に、ここまでの説明を見て「でも〜」と思った人も多いかもしれない。「こういうユースケースがカバーできるはずだ」「こう拡張すれば使えるだろう」みたいなものだ。

2011 年といえば、REST に感化された人たちが Rails で <form method=post><input type=hidden name=_method value=delete> を隠していた時代だ。なぜ自分たちが思い描く理想を、今の HTML は体現していないのかという気持ちはわからなくもない。

確かに REST は、HTTP の「ユースケースとしての側面」を開発者にわかりやすく説明していたし、Rails はそれを納得感のある形でまとめ上げていた。

一方で HTML の <form> は、ブラウザという「閲覧するため」のプラットフォームに、サーバとのインタラクションを与え「Web ページ」を「Web アプリケーション」に拡張する礎となった部品だ。Web の歴史においてもかなり早い段階に登場したものであるが、ユーザがインタラクトしている対象はリソースではなくサーバだという点では、直感的で必要十分だったと言えるだろう。

REST 信者が見ている理想的な HTTP の世界と、世界で何億人ものユーザが操作する UI のための HTML は、どちらもワークしている一方、両者には微妙なずれがある。Form は HTTP をそのままマッピングすることを想定していないし、HTTP も Form だけでコントロールされることを想定していなかったのだから当たり前と言えば当たり前だ。

一番の問題は、PUT/DELETE がサポートされていないことによって「ユーザにとっての問題が発生していない」点にある。サポートしてほしい開発者のモチベーションは、決してユーザのためではなく、開発者の満足(それを Ego というか DX というかは別として)のためという側面が色濃い。

Form というユーザが直接使う UI でありながら、「ユーザにとっての解決すべき問題」が発生していないところに「これが正しい姿だ」と提案するのは、アンチパターンの一つだ。「正しさ」が絶対評価だと思っている人は、当時 HTML の「正しい姿」を XHTML に求め、その派生で XForms を提案したが、ブラウザベンダをはじめとして誰も参加しない状態での作業を経て作成され、見向きもされずに消えていった。実際にユーザと対峙しているブラウザベンダは「絶対評価の正しさ」が存在するなどと期待していなかっただろう。「誰の何の問題を解決するためなのか」が説明されていない正しさは幻想であり、XForms もその幻想を求めた亡骸の一つだったと思う。

もし仮に PUT/DELETE をサポートするため仕様を策定しても、ブラウザはそれを実装するかわからなかったし、実装されたところでそれが広く使われるほどの需要があったかもわからない。なぜならこれも「ユーザの問題」の解決を目的としていない仕様だったからだ。

ということをあまりちゃんと説明せずに Shut-up してしまうのが Hixie という男でもある。が、彼の抱えていたワークの量を考えれば、何人かの幻想に付き合って結論が見えている議論に費やす時間はなかったのだろう。

問題は HTML か HTTP か

彼は、こんな話もしていた。

多くの開発者は <form> に HTTP メソッドが足りないと騒いでおり、まるで HTML の仕様に欠損があると考えていたが、彼にとって問題があるのは HTTP の方だった。

「もし 1990 年当時の Tim にアドバイスできたなら、HTTP をもっともっとシンプルにし、アプリケーションプロトコルはその上に載せるように言うと思う」と彼は言っていた。

実際 HTML5 以降の Web は、<form> を直接 Submit することがどんどん減っていった。なんなら、裸の <input> だけ配置し、JS で入力値をフックしたら、HTTP を POST で固定した上に GraphQL を流すような作りが増えていく。

HTTP が今のセマンティクスになったからこそ、それを正しく使いこなすという意味で REST には価値があった。その前の時代に、HTTP をトランスポートとしてしか見ていなかったために、そのポテンシャルを引き出せなかった SOAP 派(これもまた XML に幻想を抱きすぎた人たちの亡骸だ)に対する警鐘でもあった。そして、それは一定の説得力があり、実際に SOAP の時代と比べれば HTTP はだいぶ正しく使われるようになり、開発者に「HTTP のセマンティクス」という存在を意識させる上で、有意義なお祭りだったと言えるだろう。

REST が出てきた時の RPC 派の常套句は「HTTP のメソッドで足りるわけがない」だった。これは少しズレた指摘だ、REST のリソースに対するメソッド操作は、最低限のものを備えている。足りないというのは単にモデリングの失敗でしかない。モデリングは「世界をそのモデルで表す」という決め事なので、「モデル化できない」というのは「そのモデルが使いこなせなかった」でしかないはずだ。

ただ、その「モデルがユースケースにマッチしているか」は別の話だ。HTTP はステートレスであるため、それをトランザクションに落とし込む際にラウンドトリップの問題などが出てくる。こうした設計への疲弊が、今また POST で固定して GraphQL を投げるアプリへと繋がっていくことになる。

ここでモデル自体に欠点があったと決めつけたがる人も多くいるが、もし Web がこのモデルでなかったら、ここまで広く普及しなかっただろう点を考えれば、その指摘もナンセンスだろう。これがおそらく 20 年前なら SOAP をありがたがり、XHTML を信仰して、全ての予定が崩れて Web を去っていったタイプの人たちと同じく「絶対評価の正しさ」に支配された発想だと思う。

もし、このモデルを拡張しようとして HTTP に手を入れ、そこに追従するように <form> を拡張していっても、複雑だけど完璧で誰も使わない仕様が、俗にいう技術的な負債として Web に残っていた可能性の方が高い。Hixie が Shut-up せずにダラダラと議論し続け、SPA 時代に入って誰も興味を持たなくなって廃れるまで Hixie の稼働を拘束していたら、その損失は計り知れなかっただろう。

そして、その不毛な議論を収拾し、彼が当時の Tim にしたかったアドバイスを実現したのが、まさしく WebSocket だった。

HTTP によって取得されたドキュメント上で、<form> を介したユーザインタラクションの結果を、任意のアプリケーションロジックに乗せるための部品として、現代においても彼の思惑通りに使われている。

より開発者の裁量を増やす世界

Ajax 以降、アプリケーションプラットフォームとしての道を歩み続けた Web は、HTML5 以降もあらゆる機能を拡張して今に至る。

互換性を重視する性質上、今でも我々は HTTP と HTML と CSS と JS の延長で、文書閲覧で始まった頃は想定もされていなかった複雑な UI を、当時の最高峰のスペックが今の時計にすら足元にも及ばないデバイススペックの時代に、IPv4 でも十分だと考えていた人たちが想像もできないような数のユーザに対して、アプリケーションを作り続けている。

このまま HTML を拡張し続けるよりも、たとえば全てを Canvas にし、好きな言語を WASM にコンパイルし、好きな UI Library で画面を構築し、a11y は AOM で提供できるようにした方が、変化するユースケースに対応してスケールしていける。そう考えた彼が HTML を離れて始めたのが、Flutter だった。

このバックグラウンドを知れば、モバイルアプリフレームワークの一つだと思われがちな Flutter の、とりわけ Flutter for Web には、注目する価値があることがわかるだろう。

もちろん、全部 Canvas にすれば、既存のブラウザ拡張や Bookmarklet も動かないし、ソースから DOM を見てデバッグすることもできない。既存の Web が積み上げてきたものをほとんど投げ捨てるようなアイデアであるため、既存の Web スタックに愛のある人間からは、かなりの反発がある。

一方、Google Docs は去年全て Canvas ベースの実装に移り、一般ユーザにはおよそ見分けがつかない完成度でリリースされ、普通に使われている。実装はおそらく Flutter for Web ではないが、このアイデアを否定するにはあまりに大きすぎる先行事例だ。

この Hixie のアイデアを、「膨らみ続けた Web を、小さくできるかもしれない手段」として筆者が紹介したのが、JSConf JP 2019 の「Web の自重」だった。

Web の終わり方

30 年近く経つ Web は、互換性という呪縛もあってかなり大きく膨らんでいる。

「ブラウザ」の多様性は増えていくが、「ブラウザエンジンの多様性」は減る一方だ。今らかまた新しい「ブラウザエンジン」をスクラッチから作るのは、人類には不可能だろう。

大抵のものはそのくらいで限界を迎え、その横で密かに人気を得ていく何かに、ある日突然台頭されるような場面に出会う。

何がそのきっかけになるかはわからないし、どんなものに移っていくのかも想像はつかない。mozaic.fm をやっている目標の一つは、今の形の延長の Web が終わりを迎えた時、その「終わりの始まりがどこだったか」を振り返れるログを残すことだ。そのきっかけが PWA なのか、VR なのか、Web3 なのか、はたまた 3rd Party Cookie の Deprecate みたいなものなのかと、常に注視している。

先日彼は Google を去ったが、それでも Flutter は続けていくらしいと聞いてホッとした。彼も仕事もまた、今の Web の形を変えられるかもしれないポテンシャルを持つものだ。だから、彼が挑戦している先にある何かは、ぜひ最後まで見届けられると楽しいと思う。