web-dev-qa-db-ja.com

リクエストボディを含むHTTP GET

私たちのアプリケーション用に新しいRESTful Webサービスを開発しています。

特定のエンティティに対してGETを実行すると、クライアントはエンティティの内容を要求できます。パラメータを追加したい場合(たとえばリストのソートなど)、これらのパラメータをクエリ文字列に追加できます。

代わりに、私は人々がリクエストボディでこれらのパラメータを指定できるようにしたいです。 HTTP/1.1 は明示的にこれを禁止していないようです。これは彼らがより多くの情報を指定することを可能にし、複雑なXMLリクエストを指定することをより簡単にするかもしれません。

私の質問:

  • これはまったく良い考えですか?
  • HTTPクライアントは、GETリクエスト内でリクエストボディを使用することに問題がありますか?

http://tools.ietf.org/html/rfc2616

1684
Evert

GETリクエストにボディを含めることに関するロイフィールディングのコメント

はい。つまり、HTTP要求メッセージにはメッセージ本文を含めることが許可されているため、そのことを念頭に置いてメッセージを解析する必要があります。ただし、GETのサーバーセマンティクスは制限されており、ボディがある場合でも、そのリクエストに対してセマンティックな意味はありません。解析の要件は、メソッドのセマンティクスの要件とは別です。

そのため、はい、GETで本文を送信できます。いいえ、送信することは決して役に立ちません。

これはHTTP/1.1の階層化された設計の一部であり、仕様が分割されると(作業中)再び明らかになります。

....ロイ

はい、GETを使用してリクエスト本文を送信できますが、意味はありません。サーバー上で構文解析してその内容に基づいて応答を変更するで意味を与えた場合、 HTTP/1.1仕様、セクション4. のこの推奨事項は無視されます。

[...]要求メソッドにエンティティ本体の定義されたセマンティクスが含まれていない場合、メッセージ本体 SHOULD は要求の処理時に無視されます。

そして HTTP/1.1仕様、セクション9. のGETメソッドの説明:

GETメソッドは、Request-URIによって識別される情報([...])を取得することを意味します。

これは、リクエストボディはGETリクエストのリソースの識別の一部ではなく、リクエストURIのみであると述べています。

Update「HTTP/1.1仕様」として参照されるRFC2616は廃止されました。 2014年には、RFC 7230-7237に置き換えられました。 「リクエストを処理する場合、メッセージ本文を無視する必要があります」という引用は削除されました。現在は、「メソッドがメッセージ本文の使用を定義していない場合でも、要求メッセージのフレーミングはメソッドのセマンティクスに依存しません」という第2の引用「GETメソッドは、...削除されました。 -コメントから

1488
Paul Morgan

HTTP仕様で明示的に除外されていないかぎり、 can でこれを実行できますが、それを回避することをお勧めします。それは、人々がそのように動作することを期待しないからです。 HTTPリクエストチェーンには多くのフェーズがありますが、それらは「ほとんど」HTTP仕様に準拠していますが、保証されている唯一のことはそれらがWebブラウザによって使用される伝統的な振る舞いをするということです。 (私は透明なプロキシ、アクセラレータ、音声ビデオツールキットなどのことを考えています)

これが「/あなたが受け入れるものには寛大で、あなたが送るものには保守的である」という大まかな原則である Robustness Principle の背後にある精神です。

しかし、あなたには正当な理由があるのなら、それを行ってください。

253
caskey

キャッシュを利用しようとすると、問題が発生する可能性があります。プロキシは、パラメータがレスポンスに影響を与えるかどうかを確認するためにGET本文を調べることはしません。

128
Darrel Miller

restclientREST console もこれをサポートしていませんが、curlはサポートしています。

HTTP仕様 セクション4.3に書いてあります

要求メソッド(5.1.1節)の指定が要求中のエンティティボディの送信を許可していない場合、メッセージボディを要求に含めてはならない(MUST NOT)。

セクション5.1.1 は、さまざまな方法についてセクション9.xにリダイレクトします。それらのどれも明示的にメッセージ本文の包含を禁止しません。しかしながら...

セクション5.2 says

インターネット要求によって識別される正確なリソースは、Request-URIとHostヘッダーフィールドの両方を調べることによって決定されます。

そして セクション9.3 と言う

GETメソッドとは、Request-URIによって識別される情報(エンティティ形式)を取得することです。

これは、GETリクエストを処理するとき、Request-URIとHostヘッダフィールド以外のものを調べるために、サーバが required ではないことを示唆しています。

まとめると、HTTPの仕様ではGETによるメッセージ本文の送信が妨げられることはありませんが、すべてのサーバーでサポートされていない場合でも驚かないという十分なあいまいさがあります。

65
Dave Durbin

Elasticsearchは本文付きのGETリクエストを受け付けます。これが好ましい方法だとさえ思われます。 http://www.elasticsearch.org/guide/en/elasticsearch/reference/current/common-options.html#_request_body_in_query_string

(Rubyドライバのような)いくつかのクライアントライブラリは開発モードで標準出力にcryコマンドを記録することができます、そしてそれは広くこの構文を使用しています。

43
jlecour

あなたが達成しようとしていることはずっとずっと一般的な方法で長い間行われてきました、そしてそれはGETでペイロードを使用することに頼らないものです。

あなたは単にあなたの特定の検索メディアタイプを構築することができます、あるいはもっとRESTfulになりたいならば、OpenSearchのようなものを使用してください、そしてPOSTサーバが指示したURIへのリクエスト、例えば/ search。その後、サーバーは検索結果を生成するか、最終的なURIを作成して303を使用してリダイレクトできます。

これには、従来のPRG方法に従うという利点があり、キャッシュ仲介者が結果をキャッシュするのに役立ちます。

とは言っても、URIはASCIIではないもののためにとにかくエンコードされているので、application/x-www-form-urlencodedとmultipart/form-dataもそうです。あなたの意図がReSTfulシナリオをサポートすることであるならば、私はさらに別のカスタムjsonフォーマットを作成するよりもむしろこれを使用することを勧めます。

28
SerialSeb

どのサーバーがそれを無視しますか? - フィジアロン12年8月30日21:27

グーグル 例えばそれを無視するよりも悪いことをしている、それはそれを エラーと考えるだろう

簡単なnetcatを使って試してみてください。

$ netcat www.google.com 80
GET / HTTP/1.1
Host: www.google.com
Content-length: 6

1234

(1234の内容の後にCR-LFが続くので、合計6バイトになります)

そしてあなたは得るでしょう:

HTTP/1.1 400 Bad Request
Server: GFE/2.0
(....)
Error 400 (Bad Request)
400. That’s an error.
Your client has issued a malformed or illegal request. That’s all we know.

AkamaiGhostが提供するBing、Appleなどから400 Bad Requestを受け取ることもあります。

だから私はボディエンティティとGETリクエストを使用することをお勧めしません。

22
user941239

あなたは本文と共にGETを送るかPOSTを送ってRESTishの信心を捨てることができます(5年前、その信仰のメンバーは一人しかいませんでした - 彼のコメントは上でリンクされました).

どちらも素晴らしい決断ではありませんが、GETボディを送信することで、一部のクライアントや一部のサーバーの問題を防ぐことができます。

POSTを実行すると、一部のRESTishフレームワークでは問題が生じる可能性があります。

Julian Reschkeは、 "SEARCH"のような非標準のHTTPヘッダを使うことを上で提案しました。

上記のそれぞれを実行できるクライアントとできないクライアントをリストするのが最も生産的かもしれません。

ボディを含むGETを送信できないクライアント(私が知っているもの):

  • XmlHTTPRequestフィドラー

ボディを使用してGETを送信できるクライアント

  • ほとんどのブラウザ

GETからボディを取得できるサーバーとライブラリ:

  • アパッチ
  • PHP

GETから本文を削除するサーバー(およびプロキシ):

21
fijiaaron

RFC 2616、セクション4. 、「メッセージ本文」から:

サーバーは、要求に応じてメッセージ本文を読み取り、転送する必要があります。要求メソッドにエンティティ本体の定義済みセマンティクスが含まれていない場合、要求を処理するときにメッセージ本体を無視する必要があります。

つまり、サーバーは常にネットワークから提供された要求本文を読み取る必要があります(Content-Lengthを確認するか、チャンクされた本文を読み取るなど)。また、プロキシは受け取ったリクエストボディを転送する必要があります。次に、RFCが特定のメソッドの本文のセマンティクスを定義している場合、サーバーは実際に要求本文を使用して応答を生成できます。ただし、RFCが本文のセマンティクスを定義しない場合、サーバーはそれを無視する必要があります。

これは、上記のフィールディングからの引用と一致しています。

9.3項 、「GET」は、GETメソッドのセマンティクスを説明し、リクエスト本文については言及していません。したがって、サーバーは、GET要求で受信した要求本文を無視する必要があります。

17
izrik

私はこの質問をIETF HTTP WGに寄せました。 Roy Fielding(1998年のhttp/1.1文書の作者)からのコメントはそれでした

「...実装が受信された場合、その本文を解析して破棄する以外のことをするために壊されるでしょう」

RFC 7213(HTTPbis)には次のように記載されています。

「GETリクエストメッセージ内のペイロードには定義済みのセマンティクスがありません。」

その意図は、GETリクエストボディに対するセマンティックな意味が禁止されているということであることが明らかになりました。

GETにボディを含めると、 間違いなく さまざまな方法でリクエストを中断するプロキシがあります。

要約すると、それをしないでください。

14
Adrien

XMLHttpRequestによると、それは無効です。 標準から

4.5.6 send()メソッド

client . send([body = null])

要求を開始します。オプションの引数はリクエストボディを提供します。リクエストメソッドがGETまたはHEADの場合、引数は無視されます。

どちらかの状態が opened でない場合、またはsend()フラグが設定されている場合は、InvalidStateError例外が発生します。

send(body)メソッドは以下のステップを実行する必要があります。

  1. もしstateが opened でなければ、InvalidStateError例外を投げます。
  2. send()フラグが設定されている場合、InvalidStateError例外を投げます。
  3. 要求メソッドがGETまたはHEADの場合は、 body をnullに設定します。
  4. body がnullの場合は、次の手順に進みます。

ただし、GETリクエストに大きな本文コンテンツが必要になる可能性があるため、そうするべきではないと思います。

ですから、あなたがブラウザのXMLHttpRequestに頼っているのであれば、おそらくうまくいかないでしょう。

7
Rafael Sales

もしあなたが本当にキャッシュ可能なJSON/XMLボディをウェブアプリケーションに送りたいのであれば、あなたのデータを置くための唯一の合理的な場所は RFC4648:URLとファイル名安全なアルファベットによるBase 64エンコーディング でエンコードされた問い合わせ文字列です。もちろん、JSONをURLエンコードしてURLパラメータの値に格納することもできますが、Base64では結果が小さくなります。 URLサイズの制限があることに注意してください、 異なるブラウザでのURLの最大長はいくつですか? を参照してください。

あなたはBase64のパディング=文字がURLのparam値には悪いかもしれないと思うかもしれませんが、そうではないようです - この議論を見てください: http://mail.python.org/pipermail/python-bugs-list/2007-February/ 037195.html 。ただし、パディング付きのエンコードされた文字列は空の値を持つパラメータキーとして解釈されるため、パラメータ名なしでエンコードされたデータを配置しないでください。私は?_b64=<encodeddata>のようなものを使うでしょう。

7
gertas

私たちはJSONでエンコードされたencodeURIComponent(つまりURL)を送信することができます。この方法でHTTPの仕様に違反することはなく、JSONをサーバーに渡すことができます。

4
EthraZa

不適合のbase64エンコードヘッダーについてはどうですか? "SOMETHINGAPP-PARAMS:sdfSD45fdg45/aS"

長さの制限あなたのPOSTの扱いを意味の間で区別することはできませんか?ソートのような単純なパラメータが必要な場合は、なぜこれが問題になるのかわかりません。私はあなたが心配しているのは確実だと思います。

4
chaz

たとえば、Curl、Apache、PHPで動作します。

PHPファイル:

<?php
echo $_SERVER['REQUEST_METHOD'] . PHP_EOL;
echo file_get_contents('php://input') . PHP_EOL;

コンソールコマンド:

$ curl -X GET -H "Content-Type: application/json" -d '{"the": "body"}' 'http://localhost/test/get.php'

出力:

GET
{"the": "body"}
4
mnv

私はこれをお勧めしません、それは標準的な慣行に反し、そしてその見返りにそれほど提供していません。オプションではなく、コンテンツの本文を保持したいのです。

3
cloudhead

プロトコルとしてのRESTがOOPをサポートしておらず、Getメソッドが証明であることに私は戸惑います。解決策として、DTOをJSONにシリアル化してからクエリ文字列を作成できます。サーバー側では、クエリ文字列をDTOに逆シリアル化できます。

見てみましょう:

メッセージベースのアプローチは、Getメソッドの制限を解決するのに役立ちます。リクエストボディと同じように任意のDTOを送信できます

Neliburウェブサービスフレームワークはあなたが使用できる機能を提供します

var client = new JsonServiceClient(Settings.Default.ServiceAddress);
var request = new GetClientRequest
    {
        Id = new Guid("2217239b0e-b35b-4d32-95c7-5db43e2bd573")
    };
var response = client.Get<GetClientRequest, ClientResponse>(request);

as you can see, the GetClientRequest was encoded to the following query string

http://localhost/clients/GetWithResponse?type=GetClientRequest&data=%7B%22Id%22:%2217239b0e-b35b-4d32-95c7-5db43e2bd573%22%7D
2
GSerjo

GETでリクエストボディを使用するよりもはるかに優れているオプションのリストがあります。

カテゴリと各カテゴリのアイテムがあるとしましょう。どちらもIDで識別されます(この例では、 "catid"/"itemid")。特定の「順序」で別のパラメータ「sortby」に従ってソートしたいとします。あなたは "sortby"と "order"のパラメータを渡したいです。

あなたはできる:

  1. クエリ文字列を使用します。 example.com/category/{catid}/item/{itemid}?sortby=itemname&order=asc
  2. パスにはmod_rewrite(または同様のもの)を使用します。example.com/category/{catid}/item/{itemid}/{sortby}/{order}
  3. リクエストとともに渡す個々のHTTPヘッダを使う
  4. 別の方法を使用してください。 POST。リソースを取得します。

すべてに欠点がありますが、本体でGETを使用するよりもはるかに優れています。

0
Xenonite