JSONを受け入れて応答するREST APIを使用してサーバーを開発しています。問題は、クライアントからサーバーに画像をアップロードする必要がある場合です。
また、私はユースケースについて話していることに注意してください。エンティティ(ユーザー)はファイル(carPhoto、licensePhoto)を持ち、他のプロパティ(name、email ...)を持つことができます。登録プロセスの後に追加されます。
私が知っている解決策、しかしそれらのそれぞれにはいくつかの欠陥があります
1. JSONの代わりにmultipart/form-dataを使う
good:POSTおよびPUT要求は可能な限りRESTfulで、ファイルと一緒にテキスト入力を含めることができます。
cons:JSONではなくなりました。multipart/form-dataに比べてテストやデバッグなどがはるかに簡単です。
2。別々のファイルを更新することを許可する
新しいユーザーを作成するためのPOSTリクエストでは画像を追加することはできません(私たちのユースケースでは申し上げましたが大丈夫です)。PUTリクエストによってmultipart/form-dataとして/ users/4/carPhotoにアップロードされます。
good:(ファイルのアップロード自体を除く)すべてがJSONに残っているので、テストとデバッグが簡単です(完全なJSON要求を恐れずに記録できます)。彼らの長さ)
cons:それは直感的ではありません、POST、またはentityのすべての変数を一度に傾けることはできず、このアドレス/users/4/carPhoto
も考慮することができますコレクションとしての機能(REST APIの標準的な使用例は、この/users/4/shipments
のようになります)。通常、users/4/nameのように、エンティティの各変数をGET/PUTできません。 GETで名前を取得し、users/4のPUTでそれを変更することができます。 idの後に何かある場合は、通常users/4/reviewsのように別のコレクションです。
。Base64を使う
JSONとして送信しますが、Base64でファイルをエンコードします。
good:最初の解決策と同じですが、可能な限りRESTfulなサービスです。
短所:やはり、テストとデバッグはさらに悪くなり(本体には数メガバイトのデータがある可能性があります)、サイズが大きくなり、処理時間も長くなります両方 - クライアントとサーバー
私は本当に解決策番号を使用したいと思います。 2、しかしそれはその短所を持っています...誰が私に "何が最善である"という解決策のより良い洞察力を与えることができますか?
私の目標は、できる限り多くの標準を含めてRESTfulサービスを提供することですが、それをできるだけ単純にしたいのです。
OP here(私は2年後にこの質問に答えています、Daniel Cerecedoによる投稿は一度に悪くなかったのですが、Webサービスは急速に発展しています)
年間のフルタイムソフトウェア開発の後(ソフトウェアアーキテクチャ、プロジェクト管理、マイクロサービスアーキテクチャにも焦点を当てて)私は間違いなく2番目の方法(ただし1つの一般的なエンドポイント)を最善の方法として選択します。
あなたが画像のための特別な終点を持っているならば、それはあなたにそれらの画像を扱うよりはるかに大きな力を与えます。
モバイルアプリ(iOS/Android)とフロントエンド(Reactを使用)の両方に対して同じREST API(Node.js)があります。これは2017年なので、画像をローカルに保存するのではなく、アップロードしてクラウドストレージ(Googleクラウド、s3、cloudinaryなど)にしたいので、一般的な処理をしたいのです。
私たちの典型的な流れは、あなたが画像を選択するとすぐにそれがバックグラウンドでアップロードを開始し(通常POST on/imagesエンドポイント)、アップロード後にあなたにIDを返すことです。ユーザーは画像を選択してから他のいくつかのフィールド(アドレス、名前など)に進むので、これは本当にユーザーフレンドリーです。したがって、「送信」ボタンを押すと、その画像は通常すでにアップロードされています。彼は「アップロードしています...」と言って画面を見ながら待つことはしません。
画像を取得する場合も同様です。特に携帯電話と限られたモバイルデータのおかげで、元の画像を送信したくない、サイズ変更された画像を送信したい、というわけではありません。まったくリサイズするために、あなたはあなたの視野にぴったり合うイメージを望みます)。このため、良いアプリはcloudinaryのようなものを使用しています(または、サイズ変更用の独自の画像サーバーを持っています)。
また、データが非公開でない場合は、URLだけをapp/frontendに送り返して直接クラウドストレージからダウンロードするので、帯域幅とサーバーの処理時間を大幅に節約できます。私たちのより大きなアプリケーションでは毎月たくさんのダウンロードされたテラバイトがあります、あなたはあなたのREST APIサーバのそれぞれで直接それを処理したくありません、それはCRUD操作に焦点を合わせられます。あなたはそれを一つの場所(私達のImageserver、キャッシュなどを持っている)で処理したいか、クラウドサービスにそれの全てを処理させたいです。
短所:あなたが考えるべき唯一の「短所」は「割り当てられていない画像」です。ユーザーは画像を選択して他のフィールドに入力し続けますが、彼は "nah"と言ってアプリやタブをオフにしますが、その間は画像のアップロードに成功しました。これは、どこにも割り当てられていない画像をアップロードしたことを意味します。
これを処理する方法はいくつかあります。最も簡単なのは、 "I don't care"です。これがあまり頻繁に起こらない場合、または(何らかの理由で)送信したすべての画像ユーザーを保存したいという希望がある場合は、関係ありません。削除。
もう1つの方法も簡単です - あなたは毎週CRONを持っています、そしてあなたは1週間以上古い未割り当ての画像をすべて削除します。
いくつかの決定があります。
最初のリソースパスについて:
イメージをそれ自体でリソースとしてモデル化します。
ユーザーにネストされている(/ user /:id/image):ユーザーと画像の関係は暗黙的に作成されます
ルートパス(/ image)で:
クライアントは、イメージとユーザーの間の関係を確立する責任を負います。
セキュリティコンテキストが、イメージを作成するために使用されるPOST要求と共に提供されている場合、サーバーは認証されたユーザーとイメージの間の関係を暗黙的に確立することができます。
ユーザーの一部として画像を埋め込む
2番目の決定は、イメージリソースの表現方法についてです。
これが私の決断です:
base64 vs multipartの選択に関してパフォーマンスへの影響はありますか?。マルチパート形式でデータを交換する方が効率的なはずです。しかし、--- この記事 は、両方の表現のサイズの違いがほとんどないことを示しています。
私の選択Base64:
あなたの2番目の解決策はおそらく最も正しいです。意図したとおりにHTTP仕様とMIMEタイプを使用し、multipart/form-data
を介してファイルをアップロードする必要があります。関係を処理する限り、私はこのプロセスを使用します(私はあなたの仮定やシステム設計についてゼロを知っていることに留意してください)。
POST
から/users
。POST
画像を/images
に変換します。HTTP仕様に従って、画像を取得できる場所にLocation
ヘッダーを必ず返すようにしてください。PATCH
を/users/carPhoto
に変更して、ステップ2のLocation
ヘッダーに指定されている写真のIDを割り当てます。簡単な解決策はありません。それぞれの方法にはそれぞれ長所と短所があります。しかし標準的な方法は最初のオプションmultipart/form-data
を使うことです。として W3お勧めガイド と言う
コンテンツタイプ "multipart/form-data"は、ファイル、非ASCIIデータ、およびバイナリデータを含むフォームを送信するために使用する必要があります。
実際にはフォームを送信していませんが、暗黙の原則が依然として適用されます。バイナリ表現としてbase64を使用するのは、目的を達成するために間違ったツールを使用しているために間違っています。一方、2番目のオプションは、APIサービスを消費するためにAPIクライアントにより多くの仕事をさせます。使いやすいAPIを提供するには、サーバー側で大変な作業をする必要があります。最初のオプションはデバッグが簡単ではありませんが、実行してもおそらく変更されることはありません。
multipart/form-data
を使うと、REST/httpの哲学に固執することになります。あなたは同様の質問に対する答えを見ることができます こちら 。
他の方法としては、multipart/form-dataを使用することができますが、すべての値を別々に送信する代わりに、payloadという名前の値と、その中にあるjsonペイロードを送信することができます。 (私はASP.NET WebAPI 2を使用してこの方法を試してみましたが、うまくいきました)。