Nous développons un serveur avec REST API, qui accepte les réponses avec JSON. Le problème est que vous devez télécharger des images du client au serveur.
Notez également que je parle de cas d'utilisation, où entité (utilisateur) peut avoir des fichiers (carPhoto, licensePhoto) et également d'autres propriétés (nom, email, etc.), mais lorsque vous créez un nouvel utilisateur, vous n'envoyez pas ces images. , ils sont ajoutés après le processus d'inscription.
Les solutions que je connais, mais chacune d’entre elles ont des défauts
1. Utilisez multipart/form-data au lieu de JSON
good : POST et les requêtes PUT sont aussi RESTful que possible, elles peuvent contenir des entrées de texte avec le fichier.
contre : Ce n'est plus du JSON, qui est beaucoup plus facile à tester, déboguer, etc., comparé à multipart/form-data
2. Permet de mettre à jour des fichiers séparés
La requête POST pour créer un nouvel utilisateur ne permet pas d'ajouter des images (ce qui est correct dans notre cas d'utilisation comme je l'ai dit au début), le téléchargement des images est effectué par la requête PUT sous forme de multipart/form-data, par exemple,/users/4/carPhoto
good : Tout (sauf le fichier qui se télécharge lui-même) reste en JSON, il est facile de tester et de déboguer (vous pouvez enregistrer des requêtes JSON complètes sans craindre leur longueur)
inconvénients : Ce n'est pas intuitif, vous ne pouvez pas POST ou PUT toutes les variables d'une entité à la fois et cette adresse /users/4/carPhoto
peut être considéré davantage comme une collection (le cas d'utilisation standard de REST API ressemble à ceci /users/4/shipments
). Généralement, vous ne pouvez pas (et ne voulez pas) GET/PUT chaque variable d’entité, par exemple utilisateurs/4/nom. Vous pouvez obtenir le nom avec GET et le changer avec PUT à utilisateurs/4. S'il y a quelque chose après l'id, il s'agit généralement d'une autre collection, comme users/4/reviews
. Utilisez Base64
Envoyez-le en tant que JSON mais encodez les fichiers avec Base64.
good : Identique à la première solution, il s'agit d'un service aussi reposant que possible.
inconvénients : Une fois encore, le test et le débogage sont bien pires (le corps peut contenir des mégaoctets de données), la taille augmente ainsi que le temps de traitement. dans les deux - client et serveur
Je voudrais vraiment utiliser la solution no. 2, mais il a ses inconvénients ... Tout le monde peut me donner une meilleure idée de la solution "Quel est le meilleur"?
Mon objectif est de disposer de services RESTful avec autant de normes que possible, tout en souhaitant les garder aussi simples que possible.
OP ici (Je réponds à cette question après deux ans, le message de Daniel Cerecedo n'était pas mauvais à la fois, mais les services Web se développent très rapidement)
Après trois ans de développement logiciel à temps plein (avec une attention particulière portée à l'architecture logicielle, à la gestion de projet et à l'architecture de microservice), j'ai définitivement choisi la deuxième voie (mais avec un critère final) comme la meilleure.
Si vous avez un point de terminaison spécial pour les images, cela vous donne beaucoup plus de pouvoir pour gérer ces images.
Nous avons la même REST API (Node.js) pour les applications mobiles (iOS/Android) et frontales (à l'aide de React). Nous sommes en 2017. Par conséquent, vous ne souhaitez pas stocker les images localement, vous souhaitez les télécharger pour les stocker dans le cloud (Google Cloud, s3, cloudinary, ...), donc vous souhaitez un traitement général.
Notre flux typique est que, dès que vous sélectionnez une image, elle commence à télécharger en arrière-plan (généralement POST sur/images/points finaux), et vous renvoie l'identifiant après le téléchargement. C’est vraiment convivial, parce que l’utilisateur choisit une image, puis procède généralement avec d’autres champs (adresse, nom, ...). Ainsi, lorsqu’il clique sur le bouton "Envoyer", l’image est généralement déjà téléchargée. Il n'attend pas et regarde l'écran en disant "uploader ...".
La même chose vaut pour obtenir des images. Surtout grâce aux téléphones mobiles et aux données mobiles limitées, vous ne voulez pas envoyer d'images originales, vous voulez envoyer des images redimensionnées, afin qu'elles ne prennent pas trop de bande passante (et pour rendre vos applications mobiles plus rapides, vous ne voulez souvent pas pour le redimensionner, vous voulez une image qui s’intègre parfaitement dans votre vue). Pour cette raison, les bonnes applications utilisent quelque chose comme cloudinary (ou nous avons notre propre serveur d'images pour le redimensionnement).
De plus, si les données ne sont pas privées, vous renvoyez simplement à URL app/frontend et il les télécharge directement à partir du stockage en nuage, ce qui représente une économie considérable de bande passante et de temps de traitement pour votre serveur. Dans nos plus grandes applications, il y a beaucoup de téraoctets téléchargés chaque mois, vous ne voulez pas gérer cela directement sur chacun de votre serveur API REST, qui est axé sur le fonctionnement de CRUD. Vous voulez gérer cela à un endroit (notre serveur d'images, qui dispose de la mise en cache, etc.) ou laisser les services cloud gérer tout cela.
Inconvénients: Le seul "inconvénient" auquel vous devriez penser est "images non attribuées". L'utilisateur sélectionne les images et continue de remplir les autres champs, mais il dit ensuite "nah" et désactive l'application ou l'onglet, mais vous avez réussi à transférer l'image. Cela signifie que vous avez téléchargé une image qui n'est assignée nulle part.
Il y a plusieurs façons de gérer cela. Le plus simple est "Je m'en moque", ce qui est pertinent si cela ne se produit pas très souvent ou si vous avez même envie de stocker chaque image que l'utilisateur vous envoie (pour quelque raison que ce soit) et que vous n'en voulez pas. effacement.
Une autre solution est également simple: vous disposez de CRON et c’est-à-dire chaque semaine et vous supprimez toutes les images non attribuées de plus d’une semaine.
Il y a plusieurs décisions à prendre :
Le premier concernant le chemin d'accès aux ressources :
Modélisez l'image comme une ressource en soi:
Nested in user (/ user /: id/image): la relation entre l'utilisateur et l'image est faite implicitement
Dans le chemin racine (/ image):
Le client est tenu pour responsable d'établir la relation entre l'image et l'utilisateur, ou;
Si un contexte de sécurité est fourni avec la demande POST utilisée pour créer une image, le serveur peut établir implicitement une relation entre l'utilisateur authentifié et l'image.
Intégrer l'image dans le cadre de l'utilisateur
La seconde décision concerne comment représenter la ressource image :
Ce serait ma piste de décision:
Vient ensuite la question: Le choix entre base64 et multipartes at-il un impact sur les performances . Nous pourrions penser que l'échange de données en plusieurs parties devrait être plus efficace. Mais cet article montre à quel point les deux représentations diffèrent peu en termes de taille.
Mon choix base64:
Votre deuxième solution est probablement la plus correcte. Vous devez utiliser les spécifications et les types MIME HTTP comme ils étaient prévus et télécharger le fichier via multipart/form-data
. En ce qui concerne la gestion des relations, j'utiliserais ce processus (en gardant à l'esprit que je ne connais rien de vos hypothèses ou de la conception du système):
POST
à /users
pour créer l'entité utilisateur.POST
l'image à /images
, en veillant à renvoyer un en-tête Location
à l'endroit où l'image peut être récupérée conformément à la spécification HTTP.PATCH
à /users/carPhoto
et lui attribuer l'identifiant de la photo indiqué dans l'en-tête Location
de l'étape 2.Il n'y a pas de solution facile. Chaque voie a ses avantages et ses inconvénients. Mais la manière canonique utilise la première option: multipart/form-data
. Comme guide de recommandation W dit
Le type de contenu "multipart/form-data" doit être utilisé pour soumettre des formulaires contenant des fichiers, des données non-ASCII et des données binaires.
Nous n’envoyons pas de formulaires, vraiment, mais le principe implicite s’applique toujours. L'utilisation de base64 en tant que représentation binaire est incorrecte car vous utilisez un outil incorrect pour atteindre votre objectif. En revanche, la deuxième option oblige vos clients API à effectuer davantage de travaux afin de consommer votre service d'API. Vous devriez faire le travail dur côté serveur afin de fournir une API facile à utiliser. La première option n'est pas facile à déboguer, mais lorsque vous le faites, cela ne change probablement jamais.
En utilisant multipart/form-data
, vous adhérez à la philosophie REST/http. Vous pouvez voir une réponse à une question similaire ici .
Une autre option si vous mélangez les alternatives, vous pouvez utiliser multipart/form-data, mais au lieu d’envoyer chaque valeur séparément, vous pouvez envoyer une valeur nommée payload avec la charge json qu’il contient. (J'ai essayé cette approche en utilisant ASP.NET WebAPI 2 et fonctionne bien).