web-dev-qa-db-ja.com

APIの「検索」操作のクエリ文字列でJSONオブジェクトを渡すことは悪い考えですか?

UIグリッドがドメインオブジェクトのリストを検索、フィルタリング、および表示するためのAPIエンドポイントを構築しています。それらを「ウィジェット」と呼びましょう。これまでは、次のように名前付きクエリ文字列パラメーターのリストを使用してこれを作成していました。

GET /api/v1/widgets?type=2&name=what&from=2019-12-31&to=2020-01-03&pagesize=25&page=2&sort=name,-createdate

これにより、SQLは次のようになります。

SELECT <selectlist>
FROM widget
WHERE type = 2
    AND name LIKE 'what%'
    AND createdate >= '2019-12-31'
    AND createdate <= '2020-01-03'
ORDER BY name ASC, createdate DESC
LIMIT 25, 25;

パラメータの長いリストの代わりに、次のようにクエリ文字列でいくつかのJSONオブジェクトを渡すことを同僚に提案しました。

"filters": {
    { "column": "type", "operator": "=", "value": 2 },
    { "column": "name", "operator": "like", "value": "what" },
    { "column": "createdate", "operator": ">=", "value": "2019-12-31" },
    { "column": "createdate", "operator": "<=", "value": "2020-01-03" }
},
"pagination": {
    "page": "2",
    "pagesize": "25",
    "sort": [ "name", "createdate" ],
    "sortdirection": [ "asc", "desc" ]
}

これは、エンコード後、URLとして次のようになります(エンコードを間違えた場合は、例としてこれを作成しました)。

GET /api/v1/widgets?filters=[%7B%22column%22:%22type%22,%22operator%22:%22%3D%22,%22value%22:2%7D,%7B%22column%22:%22name%22,%22operator%22:%22like%22,%22value%22:%22what%22%7D,%7B%22column%22:%22createdate%22,%22operator%22:%22%3E%3D%22,%22value%22:%222019-12-31%22%7D,%7B%22column%22:%22createdate%22,%22operator%22:%22%3C%3D%22,%22value%22:%222020-01-03%22%7D]&pagination=%7B%22page%22:%222%22,%22pagesize%22:%2225%22,%22sort%22:[%22name%22,%22createdate%22],%22sortdirection%22:[%22asc%22,%22desc%22]%7D

クライアントとサーバーの両方でJavaScriptを使用しているため、JSONオブジェクトの解析は難しくありません。また、クエリ用のJSONオブジェクトの構造は、いくつかのことを考慮していないため、変更する必要があることを理解しています。それを無視して、基本的な質問は:

これは悪い/良い考えですか?長所と短所はわかりますが、個人的な偏見を乗り越えようとしています。

7
AJ Johnson

その素晴らしいアイデアではありません。 URIは、予測不可能な長さのデータに適した場所ではありません。「公式」の最大長はありませんが、多くのWebサーバーは独自の制限を適用します(たとえば、IISは2083文字です)。一部のウェブサーバーでは、URIで使用できる文字に制限があります。

URIに一般的なその他の考慮事項もあります。クエリの内容に何らかの機密性がありますか?多くのサーバーはURIとクエリ文字列パラメーターをログに記録するため、機密データが不要な場所に残される可能性があります。

この種の可変長データの場合、HTTP Postを使用するのが最善の解決策です。

それでもクエリ文字列を使用したい場合、別のオプションはJSON文字列をbase64にすることです。つまり、スペースや特殊文字のエスケープについて心配する必要はありません。これは、URIが「人間が読める」ものではなかったことを意味しますが、URIでエンコードされたJSONも特に読みにくいと主張します。

16
richzilla

悪い点は、URIで大量のデータを渡すことです。そのため、POSTのほうがいいと思います。サイズが制限されている可能性があるため、任意のデータを渡す際の問題があるため、セキュリティ上の問題があるためです。もちろん、これは簡単な変更です。JSONを生成し、それを送信する方法を決定します。

JSONのデータから効率的なクエリを生成することは難しくないことをデータベース関係者に確認してください。そうではないと思います。そして、JSONの生成は難しくありません。 JSONは、必要に応じて、より複雑なクエリを作成する機会を提供します。

4
gnasher729

元同僚とほぼ同じ分析を行いました。彼は新しいAPIエンドポイントを追加していて、個別のパラメーターではなく複雑なJSONオブジェクトをクエリ文字列に渡したいと考えていました。

あなたのように、私は予約をしました。

正確には長所と短所は、特定の製品のニーズ、アーキテクチャ、チーム構造、およびその他の要因によって異なります。

私たちのケースでは、トレードオフは単に次のとおりであることがわかりました。

プロ:API実装でコードの半分の行を保存

短所:

  • Swagger(APIドキュメントツール)には、このAPIの構造をドキュメント化する直感的な方法はありません。 APIドキュメント、アクセシビリティ、および発見可能性は私たちにとって重要でした。
  • 世界の他の地域と矛盾しています。一般的な慣習と期待を守ることで、物事が簡素化され、間違いを防ぐことができます。

とはいえ、このパターンが必ずしも悪いとは思いません。検索フィルターの動的セットを作成する場合など、特定のケースでは、それが理にかなっている場合があります。あなたのユースケースはこれの良い候補かもしれません。

他の回答で受け取ったGETに対する差し戻しには完全に同意しません。 GETは、データを取得しているため、意味的に正しいHTTP動詞です。状態を変更していません。それはべき等であり、キャッシュ可能です。 POSTはそうではありません。

GETリクエストはPOSTリクエストと同様に本文を受け入れることができ、POSTリクエストはクエリ文字列を受け入れることができます。 GETはクエリ文字列を意味し、POSTは本文を意味するという誤解があります。それらは直交しています。

あなたの状況で私が考慮したい要素:

  • 文書化可能性(組織や製品にとって重要な場合)
  • ツール-ほとんどのHTTPルーティングフレームワークには、クエリ文字列パラメーターを自動的に解析してマップとして公開するミドルウェアがあります。たとえば、node.jsエクスプレスアプリでは、req.query.filtersをチェックしてフィルターを取得できます。 JSONルートを使用する場合は、独自の解析を行う必要がありますが、それは難しくありません。これは単純なJSON.parse呼び出しです。
  • 検証-指定したスキーマに基づいてリクエストを自動的に検証できるモジュールがあります。入力をJSONオブジェクトに移動すると、ブラックボックスが作成され、入力を自分で検証する必要があります(入力の検証が不十分であることは、セキュリティの脆弱性の主な原因の1つであることは誰もが知っています)
  • 複雑さ-無限のフィルター、またはフィルターの動的リストをサポートする必要がありますか?オペレーターはそのように構成可能である必要がありますか?このJSONオブジェクトの設計は非常に拡張可能です。フィルター処理するものが4つしかなく、それらが常に同じように機能する場合、ハードコーディングによって時間を大幅に節約できます。
  • テストのオーバーヘッド-上記の複雑なポイントに続いて、基準のすべての組み合わせをテストする必要があります。 APIで任意のフィールドの任意の演算子が許可されている場合は、これらの各ケースをテストする必要があります。クライアントが単一の方法でのみAPIを使用する場合、サポートが行き詰まっている未使用のユースケースがたくさんあります。たとえば、フロントエンドが常にワイルドカード検索を実行する場合、name=ケースのテストは実際には使用されないため無駄ですが、APIはそれをサポートしているため、より適切に機能します。

このアプローチに関する私のすべての懸念にもかかわらず、ElasticsearchはJSONパターンを実行し、それらに対して非常にうまく機能します。ただし、Elasticsearchは検索エンジンです。渡されるフィルター、演算子などのセットは動的である必要があります。JSON構造は実際には検索クエリを意図しているためです。 Elasticsearchはあらゆる種類のユーザー定義スキーマをサポートするため、一般的なクエリ言語をJSONとして公開します。

Webページにいくつかの入力があり、それらが既知のSQL述語に直接マッピングされている場合、JSONパターンでやりすぎている可能性があります。

これが、製品が実際に何であるかが本当に重要である理由です。

あなたはいつかあるかもしれませんという問題ではなく、あなたが抱えている問題を解決してください。

2
Brandon

RFC 7320のセクション2.4 を参照)を使用する必要がないことに注意してください(&および=)(クエリ文字列については、 URL標準セクション5 を参照)、許容文字のみを使用している限り、フォーマットを構成できます(文字、数字、-._~!$&'()*+,;=:@/?RFC 3986 を参照))。

0
Solomon Ucko