Rozwijamy serwer z interfejsem API REST, który przyjmuje i odpowiada za pomocą JSON. Problem polega na tym, że jeśli chcesz przesłać obrazy z klienta na serwer.
Uwaga: mówię również o przypadku użycia, w którym podmiot (użytkownik) może mieć wiele plików (carPhoto, licensePhoto), a także mieć inne właściwości (imię i nazwisko, adres e-mail ...), ale kiedy tworzysz nowego użytkownika, nie nie wysyłają tych zdjęć, są one dodawane po procesie rejestracji.
Znane mi rozwiązania, ale każde z nich ma pewne wady
1. Użyj danych wieloczęściowych / formularzy zamiast JSON
dobrze : Żądania POST i PUT są tak RESTful jak to możliwe, mogą zawierać dane tekstowe wraz z plikiem.
Wady : To już nie jest JSON, który jest znacznie łatwiej testować, debugować itp. w porównaniu do danych wieloczęściowych / formularzy
2. Pozwól zaktualizować osobne pliki
Żądanie POST dotyczące utworzenia nowego użytkownika nie pozwala na dodawanie zdjęć (co jest ok w naszym przypadku użycia, jak powiedziałem na początku), przesyłanie zdjęć odbywa się za pomocą żądania PUT jako multipart / form-data na przykład / users / 4 / carPhoto
dobrze : wszystko (oprócz samego przesyłania pliku) pozostaje w JSON, jest łatwe do testowania i debugowania (możesz rejestrować pełne żądania JSON bez obawy o ich długość)
Wady : nie jest intuicyjne, nie można jednocześnie POST lub PUT wszystkich zmiennych bytu, a także ten adres /users/4/carPhoto
można traktować bardziej jako zbiór (wygląda to tak jak w przypadku standardowego interfejsu API REST /users/4/shipments
). Zwykle nie możesz (i nie chcesz) GET / PUT każdej zmiennej bytu, na przykład users / 4 / name. Możesz uzyskać nazwę za pomocą GET i zmienić ją za pomocą PUT na users / 4. Jeśli po identyfikatorze jest coś, zwykle jest to inna kolekcja, na przykład users / 4 / reviews
3. Użyj Base64
Wyślij jako JSON, ale koduj pliki za pomocą Base64.
dobrze : Tak samo jak pierwsze rozwiązanie, jest to usługa RESTful, jak to możliwe.
Wady : Po raz kolejny testowanie i debugowanie jest znacznie gorsze (ciało może mieć megabajty danych), zwiększa się rozmiar, a także czas przetwarzania zarówno w kliencie, jak i serwerze
Naprawdę chciałbym skorzystać z rozwiązania no. 2, ale ma swoje wady ... Czy ktoś może lepiej zrozumieć rozwiązanie „co jest najlepsze”?
Moim celem jest udostępnienie usług RESTful przy możliwie jak największej liczbie standardów, a ja chcę, aby było to tak proste, jak to możliwe.
Odpowiedzi:
OP tutaj (odpowiadam na to pytanie po dwóch latach, post napisany przez Daniela Cerecedo nie był wcale zły, ale serwisy internetowe rozwijają się bardzo szybko)
Po trzech latach rozwoju oprogramowania w pełnym wymiarze godzin (koncentrując się również na architekturze oprogramowania, zarządzaniu projektami i architekturze mikrousług) zdecydowanie wybrałem drugi sposób (ale z jednym ogólnym punktem końcowym) jako najlepszy.
Jeśli masz specjalny punkt końcowy dla obrazów, daje to o wiele więcej możliwości zarządzania nimi.
Mamy ten sam interfejs API REST (Node.js) zarówno dla aplikacji mobilnych (iOS / Android), jak i interfejsu użytkownika (przy użyciu React). Jest rok 2017, dlatego nie chcesz przechowywać zdjęć lokalnie, chcesz przesłać je do niektórych pamięci w chmurze (Google Cloud, S3, Cloudinary, ...), dlatego chcesz trochę ogólnej obsługi nad nimi.
Nasz typowy przepływ polega na tym, że jak tylko wybierzesz obraz, zaczyna on przesyłać w tle (zwykle POST na / obrazach końcowych), zwracając ci identyfikator po przesłaniu. Jest to bardzo przyjazne dla użytkownika, ponieważ użytkownik wybiera obraz, a następnie zwykle przechodzi do innych pól (tj. Adres, nazwa, ...), dlatego gdy naciśnie przycisk „wyślij”, obraz jest zwykle już przesłany. Nie czeka i patrzy na ekran z napisem „przesyłanie ...”.
To samo dotyczy uzyskiwania zdjęć. Zwłaszcza dzięki telefonom komórkowym i ograniczonej ilości danych mobilnych nie chcesz wysyłać oryginalnych zdjęć, chcesz przesyłać obrazy o zmienionym rozmiarze, więc nie zajmują one tak dużej przepustowości (a aby Twoje aplikacje mobilne działały szybciej, często nie chcesz aby w ogóle go zmienić, chcesz obraz idealnie pasujący do twojego widoku). Z tego powodu dobre aplikacje używają czegoś takiego jak cloudinary (lub mamy własny serwer obrazów do zmiany rozmiaru).
Ponadto, jeśli dane nie są prywatne, odsyłasz do adresu URL aplikacji / interfejsu użytkownika, który pobiera go bezpośrednio z pamięci w chmurze, co jest ogromną oszczędnością przepustowości i czasu przetwarzania dla twojego serwera. W naszych większych aplikacjach pobieranych jest co miesiąc wiele terabajtów, nie chcesz obsługiwać tego bezpośrednio na każdym serwerze REST API, który koncentruje się na działaniu CRUD. Chcesz sobie z tym poradzić w jednym miejscu (nasz Imageserver, który ma buforowanie itp.) Lub pozwolić usługom w chmurze obsłużyć to wszystko.
Wady: Jedyne „wady”, o których powinieneś pomyśleć, to „nieprzypisane obrazy”. Użytkownik wybiera zdjęcia i kontynuuje wypełnianie innych pól, ale potem mówi „nie” i wyłącza aplikację lub kartę, ale w międzyczasie udało się załadować obraz. Oznacza to, że przesłałeś zdjęcie, które nie jest nigdzie przypisane.
Istnieje kilka sposobów radzenia sobie z tym. Najłatwiejszym z nich jest „nie dbam”, co jest istotne, jeśli nie dzieje się to zbyt często lub nawet chcesz przechowywać każdego wysłanego przez użytkownika obrazu (z dowolnego powodu) i nie chcesz żadnego usunięcie.
Kolejny jest również łatwy - masz CRON, tj. Co tydzień i usuwasz wszystkie nieprzypisane zdjęcia starsze niż tydzień.
źródło
Należy podjąć kilka decyzji :
Pierwsza o ścieżce zasobów :
Sam modeluj obraz jako zasób:
Zagnieżdżone w użytkowniku (/ user /: id / image): relacja między użytkownikiem a obrazem jest niejawna
W ścieżce katalogu głównego (/ image):
Klient ponosi odpowiedzialność za ustanowienie relacji między obrazem a użytkownikiem, lub;
Jeśli kontekst bezpieczeństwa jest dostarczany z żądaniem POST użytym do utworzenia obrazu, serwer może pośrednio ustanowić relację między uwierzytelnionym użytkownikiem a obrazem.
Osadź obraz jako część użytkownika
Druga decyzja dotyczy sposobu reprezentowania zasobu obrazu :
To byłaby moja ścieżka decyzyjna:
Potem pojawia się pytanie: czy ma jakiś wpływ na wydajność przy wyborze base64 vs multipart? . Można by pomyśleć, że wymiana danych w formacie wieloczęściowym powinna być bardziej wydajna. Ale ten artykuł pokazuje, jak niewiele różnią się obie reprezentacje pod względem wielkości.
Mój wybór Base64:
źródło
Twoje drugie rozwiązanie jest prawdopodobnie najbardziej poprawne. Powinieneś użyć specyfikacji HTTP i typów mimetycznych w sposób, w jaki były zamierzone, i przesłać plik przez
multipart/form-data
. Jeśli chodzi o obsługę relacji, skorzystałbym z tego procesu (pamiętając, że znam zero na temat twoich założeń lub projektu systemu):POST
aby/users
utworzyć encję użytkownika.POST
obraz do/images
, zwracającLocation
nagłówek do miejsca, w którym można pobrać obraz zgodnie ze specyfikacją HTTP.PATCH
do/users/carPhoto
i przypisać mu identyfikator danego zdjęcie wLocation
nagłówku kroku 2.źródło
Nie ma łatwego rozwiązania. Każda droga ma swoje zalety i wady. Ale kanoniczny sposób korzysta pierwszą opcję:
multipart/form-data
. Jak mówi przewodnik rekomendacji W3Tak naprawdę nie wysyłamy formularzy, ale zasada domyślna nadal obowiązuje. Używanie base64 jako reprezentacji binarnej jest niepoprawne, ponieważ używasz niewłaściwego narzędzia do osiągnięcia celu, z drugiej strony druga opcja zmusza klientów API do wykonania większej ilości zadań w celu wykorzystania usługi API. Powinieneś ciężko pracować po stronie serwera, aby dostarczyć łatwego w obsłudze interfejsu API. Pierwsza opcja nie jest łatwa do debugowania, ale kiedy to zrobisz, prawdopodobnie nigdy się nie zmienia.
Korzystając z programu,
multipart/form-data
trzymasz się filozofii REST / http. Można zobaczyć odpowiedzi na podobne pytanie tutaj .Inną opcją jest mieszanie alternatyw, można użyć danych wieloczęściowych / formularzy, ale zamiast wysyłać każdą wartość osobno, można wysłać wartość o nazwie ładunek z ładunkiem json w środku. (Próbowałem tego podejścia przy użyciu ASP.NET WebAPI 2 i działa dobrze).
źródło