API REST - fichier (c'est à dire des images) traitement - les meilleures pratiques

nous développons l'API server avec REST, qui accepte et répond avec JSON. Le problème est, si vous avez besoin de télécharger des images d'un client à un serveur.

Notez aussi que je parle de use-case, où entity (user) peut avoir des fichiers (carPhoto, licensePhoto) et aussi avoir d'autres propriétés (Nom, email...), mais lorsque vous créez un nouvel utilisateur, vous n'avez pas envoyer ces images, elles sont ajoutés après le processus d'enregistrement.


Les solutions je suis au courant, mais chacun d'entre eux ont des défauts

1. Utilisez multipart / form-data au lieu de JSON

good : les requêtes POST et PUT sont aussi reposantes que possible, elles peuvent contenir des entrées de texte avec le fichier.

cons : ce n'est plus JSON, ce qui est beaucoup plus facile à tester, déboguer, etc. comparer avec multipart / form-data

2. Permet de mettre à jour les fichiers séparés

POST demande pour la création d'un nouvel utilisateur ne permet pas d'ajouter des images (ce qui est normal dans notre cas d'utilisation comment je l'ai dit au début), en téléchargeant des images se fait par requête PUT que multipart/form-data par exemple /users/4/carPhoto

bon : tout (sauf le téléchargement du fichier lui-même) reste dans JSON, il est facile de tester et de déboguer (vous pouvez journal complet JSON demandes sans avoir peur de leur longueur)

cons : ce N'est pas intuitif, vous ne pouvez pas poster ou mettre toutes les variables d'entity à la fois et aussi cette adresse /users/4/carPhoto peut être considérée plus comme une collection (standard use-case for REST API ressemble à ce /users/4/shipments ). Habituellement, vous ne pouvez pas (et ne voulez pas) obtenir/mettre chaque variable d'entité, par exemple users/4/name . Vous pouvez obtenir le nom avec GET et le changer avec PUT at users/4. S'il y a quelque chose après l'id, c'est généralement une autre collection, comme users /4 / reviews

3. Usebase De Données64 "1519120920

L'envoie comme JSON mais encode des fichiers avec Base64.

bon : même que la première solution, il est le service aussi reposant que possible.

contre : encore une fois, le test et le débogage sont bien pires (le corps peut avoir des mégaoctets de données), il ya une augmentation de la taille et aussi dans le temps de traitement à la fois dans le client et le serveur


je voudrais vraiment utiliser la solution no. 2, mais il a ses inconvénients... N'importe qui peut me donner un meilleur aperçu de la solution "what is best"?

mon objectif est d'avoir des services reposants avec autant de normes incluses que possible, tout en maintenant aussi simple que possible.

67
demandé sur libik 2015-10-22 13:44:38

4 réponses

il y a plusieurs décisions à prendre :

  1. Le premier sujet de chemin d'accès aux ressources :

    • modéliser l'image en tant que ressource propre:

      • Imbriquée dans de l'utilisateur (/utilisateur/:id/image): la relation entre l'utilisateur et l'image est faite implicitement

      • dans la chemin racine (/image):

        • le client est tenu responsable de l'établissement de la relation entre l'image et l'utilisateur, ou

        • si un contexte de sécurité est fourni avec la requête POST utilisée pour créer une image, le serveur peut implicitement établir une relation entre l'utilisateur authentifié et l'image.

    • intègre l'image en tant que partie de l'utilisateur

  2. la deuxième décision porte sur comment représenter la ressource d'image :

    • Comme Base 64 encodé en JSON charge
    • Comme un multipart charge

ce serait ma voie de décision:

  • en général, je préfère la conception à la performance, à moins qu'il n'y ait de bonnes raisons de le faire. Il rend le système plus facile à entretenir et peut être plus facilement compris par les intégrateurs.
  • donc ma première pensée est d'aller pour une représentation Base64 de la ressource image parce qu'elle vous permet de garder tout JSON. Si vous choisissez cette option, vous pouvez modéliser le chemin de la ressource comme vous le souhaitez.
    • si la relation entre l'utilisateur et l'image est de 1 à 1, Je favorisez la modélisation de l'image en tant qu'attribut, surtout si les deux ensembles de données sont mis à jour en même temps. Dans tous les autres cas, vous pouvez librement choisir de modéliser l'image soit en tant qu'attribut, en mettant à jour L'it via PUT ou PATCH, ou en tant que ressource séparée.
  • si vous choisissez la charge utile multipart, je me sentirais obligé de modéliser l'image comme une ressource propre, de sorte que d'autres ressources, dans notre cas, la ressource utilisateur, ne soit pas affectée par la décision d'utiliser une représentation binaire de l'image.

vient ensuite la question: est-ce que le choix de base64 vs multipart a un impact sur les performances? . Nous pourrions penser que l'échange de données dans un format multipartite devrait être plus efficace. Mais cet article montre combien peu les deux représentations diffèrent en termes de taille.

Mon choix Base64:

  • conception cohérente décision
  • négligeable performance impact
  • comme les navigateurs comprennent les URIs de données (images encodées base64), il n'est pas nécessaire de les transformer si le client est un navigateur
  • Je ne voterai pas pour savoir s'il s'agit d'un attribut ou d'une ressource autonome, cela dépend de votre domaine de problèmes (que je ne sais pas) et de vos préférences personnelles.
63
répondu Daniel Cerecedo 2015-10-27 16:37:03

OP here (je réponds à cette question après deux ans, le post fait par Daniel Cerecedo n'était pas mauvais à un moment, mais les services web se développent très rapidement)

après trois ans de développement de logiciels à temps plein (avec un accent sur l'architecture logicielle, la gestion de projet et l'architecture microservice) je choisis certainement la deuxième voie (mais avec un point final général) comme la meilleure.

si vous avez un endpoint spécial pour les images, il vous donne beaucoup plus de pouvoir sur la manipulation de ces images.

nous avons la même API REST (Node.js) pour les applications mobiles (iOS / android) et frontend (en utilisant React). C'est 2017, donc vous ne voulez pas stocker des images localement, vous voulez les TÉLÉCHARGER pour venir stockage cloud (Google cloud, S3, cloudinary, ...), par conséquent vous voulez une certaine manipulation générale sur eux.

Notre flux typique est, que dès que vous sélectionnez image, il commence à télécharger sur l'arrière-plan (généralement POST on /image endpoint), vous retournant L'ID après avoir téléchargé. C'est vraiment facile à utiliser, parce que l'utilisateur choisit l'image et puis généralement procéder avec quelques autres champs (c.-à-d. adresse, nom, ...), donc quand il frappe bouton "envoyer", l'image est généralement déjà téléchargé. Il n'attend pas et regarde l'écran en disant "uploadiing...".

il en va de même pour obtenir des images. Surtout grâce aux téléphones portables et les données mobiles limitées, vous ne voulez pas envoyer des images originales, vous voulez envoyer des images redimensionnées, de sorte qu'ils ne prennent pas beaucoup de données (et pour rendre vos applications mobiles plus rapides, vous souvent ne voulez pas redimensionner du tout, vous voulez une image qui s'adapte parfaitement dans votre vue). Pour cette raison, les bonnes applications utilisent quelque chose comme cloudinary (ou nous avons notre propre serveur d'image pour le redimensionnement).

aussi, si les données ne sont pas privées, alors vous renvoyez à app / frontend juste URL et il téléchargements il à partir du stockage en nuage directement, ce qui représente une énorme économie 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 D'API REST, qui est axé sur le fonctionnement CRUD. Vous voulez gérer cela à un endroit (Notre Imageserver, qui ont la mise en cache etc.) ou laisser les services en nuage s'occuper de tout.


contre : le seul "contre" que vous devriez pensez à "images non attribuées". L'utilisateur sélectionne les images et continue à remplir d'autres champs, mais ensuite il dit "nah" et éteint l'application ou l'onglet, mais pendant ce temps vous avez téléchargé avec succès 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 facile est "I dont care", qui est pertinent un, si ce n'est pas se produire très souvent ou vous avez même le désir de stocker chaque utilisateur d'image vous envoyer (pour tout raison) et vous ne voulez pas de suppression.

un autre est facile aussi - vous avez CRON et c.-à-d. chaque semaine et vous supprimez toutes les images non affectées plus d'une semaine.

54
répondu libik 2017-06-08 15:03:11

Votre deuxième solution est probablement la plus correcte. Vous devez utiliser les spécifications HTTP et les mimetypes de la façon dont ils ont été conçus 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 sais rien de vos hypothèses ou de la conception du système):

  1. POST à /users pour créer l'entité utilisateur.
  2. POST l'image /images , s'assurer de retour un en-tête Location où l'image peut être récupérée selon les spécifications HTTP.
  3. PATCH à /users/carPhoto et lui attribuer L'ID de la photo donnée dans l'en-tête Location de l'étape 2.
7
répondu mam8cc 2015-10-22 15:55:16

il n'y a pas de solution facile. Chaque voie a ses avantages et ses inconvénients . Mais la voie canonique utilise la première option: multipart/form-data . Comme W3 guide de recommandation dit

le type de contenu" multipart/form-data " doit être utilisé pour soumettre des formulaires qui contiennent des fichiers, des données non ASCII et des données binaires.

nous n'envoyons pas vraiment de formulaires,mais le principe implicite s'applique toujours. Utilisation de base64 comme une représentation binaire, est incorrecte parce que vous utilisez le mauvais outil pour accomplir votre but, en revanche, la deuxième option oblige vos clients API à faire plus de travail afin de consommer votre service API. Vous devez faire le travail dur du côté du serveur afin de fournir une API facile à consommer. La première option n'est pas facile à déboguer, mais quand vous le faites, il probablement ne change jamais.

en utilisant multipart/form-data vous êtes collé avec le reste/la philosophie http. Vous pouvez afficher une réponse à la 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 charge utile avec la charge utile json à l'intérieur. (J'ai essayé cette approche en utilisant ASP.NET WebAPI 2 et fonctionne bien).

2
répondu Kellerman Rivero 2017-05-23 12:18:21