<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>Web API</title><link>https://blog.ast.moe/tags/web-api/</link><description>Recent posts from 五里霧中</description><generator>Hugo</generator><item><title>HTTPにQUERYメソッドが追加されたのでRFC 10008を読んだ</title><link>https://blog.ast.moe/blog/2026-09-15-3/</link><pubDate>Tue, 15 Sep 2026 10:03:58 +0900</pubDate><guid>https://blog.ast.moe/blog/2026-09-15-3/</guid><description>&lt;p&gt;HTTPに&lt;code&gt;QUERY&lt;/code&gt;というメソッドが追加されていました。&lt;/p&gt;
&lt;p&gt;2026年6月に公開された&lt;a href="https://www.rfc-editor.org/rfc/rfc10008.html"&gt;RFC 10008「The HTTP QUERY Method」&lt;/a&gt;です。RFC 1008ではなく、ついに5桁へ入ったほうのRFC 10008です。&lt;/p&gt;
&lt;p&gt;名前のとおりサーバーへ問い合わせるためのメソッドで、検索条件をリクエストボディへ入れられます。&lt;/p&gt;
&lt;p&gt;それだけならPOSTでよさそうにも見えます。実際、複雑な検索APIをPOSTで作ることは珍しくありません。&lt;/p&gt;
&lt;p&gt;QUERYが追加された理由は、ボディを送れることだけではなく、そのリクエストが読み取り専用で、同じ内容を再送してもよいとHTTP上で明示できるところにあります。&lt;/p&gt;
&lt;h2 id="getで検索すると条件がuriへ集まる"&gt;GETで検索すると条件がURIへ集まる&lt;/h2&gt;
&lt;p&gt;単純な検索なら、GETのクエリパラメーターで困りません。&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-http" data-lang="http"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;GET&lt;/span&gt; /feed?q=foo&amp;amp;limit=10&amp;amp;sort=-published &lt;span style="color:#66d9ef"&gt;HTTP&lt;/span&gt;&lt;span style="color:#f92672"&gt;/&lt;/span&gt;&lt;span style="color:#ae81ff"&gt;1.1&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;Host&lt;span style="color:#f92672"&gt;:&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;example.org&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;ただ、検索条件が増えて入れ子になったり、ある程度まとまった式を渡したくなったりすると、URIへ全部詰めるのが苦しくなります。&lt;/p&gt;
&lt;p&gt;URIの長さについて、経路上にいるすべてのクライアント、プロキシ、CDN、サーバーが共有する単純な上限があるわけではありません。長いURIをどこまで通せるかは、実際の構成に左右されます。&lt;/p&gt;
&lt;p&gt;JSONなどの構造をURIとして扱える形へエンコードする手間もあります。また、URIはアクセスログや履歴などへ残りやすいため、検索条件に扱いづらい情報が含まれる場合もあります。&lt;/p&gt;
&lt;p&gt;ではGETにボディを付ければよいのかというと、これも標準的な解決にはなりません。&lt;/p&gt;
&lt;p&gt;&lt;a href="https://www.rfc-editor.org/rfc/rfc9110.html#section-9.3.1"&gt;RFC 9110のGETの定義&lt;/a&gt;では、GETで受け取ったボディには一般的に定義された意味がありません。一部の実装ではrequest smugglingの可能性を理由に拒否されることもあり、事前に対応が分かっているオリジンサーバーへ直接送る場合を除いて、クライアントはGETにボディを付けるべきではないとされています。&lt;/p&gt;
&lt;p&gt;送信元とアプリケーションが対応していても、途中にある別の実装まで同じ解釈をするとは限りません。&lt;/p&gt;
&lt;h2 id="postならボディを送れる"&gt;POSTならボディを送れる&lt;/h2&gt;
&lt;p&gt;GETで扱いづらい検索をPOSTにする方法は、すでに広く使われています。&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-http" data-lang="http"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;POST&lt;/span&gt; /feed/search &lt;span style="color:#66d9ef"&gt;HTTP&lt;/span&gt;&lt;span style="color:#f92672"&gt;/&lt;/span&gt;&lt;span style="color:#ae81ff"&gt;1.1&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;Host&lt;span style="color:#f92672"&gt;:&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;example.org&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;Content-Type&lt;span style="color:#f92672"&gt;:&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;application/json&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;{
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;&amp;#34;query&amp;#34;&lt;/span&gt;: &lt;span style="color:#e6db74"&gt;&amp;#34;foo&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;&amp;#34;limit&amp;#34;&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;10&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;&amp;#34;sort&amp;#34;&lt;/span&gt;: &lt;span style="color:#e6db74"&gt;&amp;#34;-published&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;これなら複雑な条件をボディへ入れられます。&lt;/p&gt;
&lt;p&gt;一方でPOSTは、リソースの作成や状態変更にも使われるメソッドです。そのPOSTが実際には読み取り専用だったとしても、HTTPの一般的な仕組みからは、安全で何度でも再送できる問い合わせなのか判断できません。&lt;/p&gt;
&lt;p&gt;接続が切れたときに自動で再試行してよいのか、レスポンスを同じ問い合わせへ再利用してよいのかといった扱いには、そのAPI固有の知識が必要になります。&lt;/p&gt;
&lt;p&gt;QUERYは、ここをHTTPメソッドの意味として定義します。&lt;/p&gt;
&lt;h2 id="queryはボディを持つ安全で冪等なメソッド"&gt;QUERYはボディを持つ安全で冪等なメソッド&lt;/h2&gt;
&lt;p&gt;QUERYで同じ検索を書くと、次のようになります。&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;QUERY /feed HTTP/1.1
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;Host: example.org
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;Content-Type: application/json
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;Accept: application/json
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;{
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &amp;#34;query&amp;#34;: &amp;#34;foo&amp;#34;,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &amp;#34;limit&amp;#34;: 10,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &amp;#34;sort&amp;#34;: &amp;#34;-published&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;問い合わせの内容はリクエストボディと&lt;code&gt;Content-Type&lt;/code&gt;によって決まり、対象となる範囲はリクエスト先のリソースによって決まります。&lt;/p&gt;
&lt;p&gt;GET、QUERY、POSTの違いを大まかに並べると次のようになります。&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;性質&lt;/th&gt;
&lt;th&gt;GET&lt;/th&gt;
&lt;th&gt;QUERY&lt;/th&gt;
&lt;th&gt;POST&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;安全&lt;/td&gt;
&lt;td&gt;はい&lt;/td&gt;
&lt;td&gt;はい&lt;/td&gt;
&lt;td&gt;保証されない&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;冪等&lt;/td&gt;
&lt;td&gt;はい&lt;/td&gt;
&lt;td&gt;はい&lt;/td&gt;
&lt;td&gt;保証されない&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;リクエストボディの意味&lt;/td&gt;
&lt;td&gt;定義されていない&lt;/td&gt;
&lt;td&gt;問い合わせとして使う&lt;/td&gt;
&lt;td&gt;対象リソース次第&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;レスポンスのキャッシュ&lt;/td&gt;
&lt;td&gt;可能&lt;/td&gt;
&lt;td&gt;可能&lt;/td&gt;
&lt;td&gt;制限あり&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;ここでいう安全とは、クライアントが対象リソースの状態変更を要求しないという意味です。アクセスログが増える、計算資源を使う、問い合わせ結果を表す一時的なリソースをサーバーが作る、といった副作用まで禁止するものではありません。&lt;/p&gt;</description></item></channel></rss>