Je veux envoyer un client Article depuis et Android vers un serveur REST. Voici le Python modèle du serveur:
class Article(models.Model):
author = models.CharField(max_length=256, blank=False)
photo = models.ImageField()
L'interface suivante décrit l'ancienne implémentation:
@POST("/api/v1/articles/")
public Observable<CreateArticleResponse> createArticle(
@Body Article article
);
Maintenant, je veux envoyer une image avec les données Article. Le photo
ne fait pas partie du modèle Article sur le client Android.
@Multipart
@POST("/api/v1/articles/")
public Observable<CreateArticleResponse> createArticle(
@Part("article") Article article,
@Part("photo") TypedFile photo
);
L'API est préparée et testée avec succès avec cURL.
$ curl -vX POST http://localhost:8000/api/v1/articles/ \
-H "Content-Type: multipart/form-data" \
-H "Accept:application/json" \
-F "author=cURL" \
-F "photo=@/home/user/Desktop/article-photo.png"
Lorsque j'envoie des données via createArticle()
depuis le client Android, je reçois un état HTTP 400
Indiquant que les champs sont obligatoires/manquant .
D <--- HTTP 400 http://192.168.1.1/articles/ (2670ms)
D Date: Mon, 20 Apr 2015 12:00:00 GMT
D Server: WSGIServer/0.1 Python/2.7.8
D Vary: Accept, Cookie
D X-Frame-Options: SAMEORIGIN
D Content-Type: application/json
D Allow: GET, POST, HEAD, OPTIONS
D OkHttp-Selected-Protocol: http/1.0
D OkHttp-Sent-Millis: 1429545450469
D OkHttp-Received-Millis: 1429545453120
D {"author":["This field is required."],"photo":["No file was submitted."]}
D <--- END HTTP (166-byte body)
E 400 BAD REQUEST
Voici ce qui est reçu en tant que request.data
Côté serveur:
ipdb> print request.data
<QueryDict: {u'article': [u'{"author":"me"}'], \
u'photo': [<TemporaryUploadedFile: IMG_1759215522.jpg \
(multipart/form-data)>]}>
Comment convertir l'objet Article dans un type de données conforme à plusieurs parties? J'ai lu que Retrofit pourrait permettre d'utiliser Converters pour cela. Cela devrait être quelque chose qui implémente un retrofit.mime.TypedOutput
Pour autant que j'ai compris pour le documentation .
Les pièces en plusieurs parties utilisent le convertisseur de
RestAdapter
ou elles peuvent implémenterTypedOutput
pour gérer leur propre sérialisation.
Selon votre demande de boucle, vous essayez de créer un smth comme ceci:
POST http://localhost:8000/api/v1/articles/ HTTP/1.1
User-Agent: curl/7.30.0
Host: localhost
Connection: Keep-Alive
Accept: application/json
Content-Length: 183431
Expect: 100-continue
Content-Type: multipart/form-data; boundary=----------------------------23473c7acabb
------------------------------23473c7acabb
Content-Disposition: form-data; name="author"
cURL
------------------------------23473c7acabb
Content-Disposition: form-data; name="photo"; filename="article-photo.png"
Content-Type: application/octet-stream
‰PNG
<!RAW BYTES HERE!>
M\UUÕ+4qUUU¯°WUUU¿×ß¿þ Naa…k¿ IEND®B`‚
------------------------------23473c7acabb--
Avec l'adaptateur de modification, cette demande peut être créée de la manière suivante:
@Multipart
@POST("/api/v1/articles/")
Observable<Response> uploadFile(@Part("author") TypedString authorString,
@Part("photo") TypedFile photoFile);
Usage:
TypedString author = new TypedString("cURL");
File photoFile = new File("/home/user/Desktop/article-photo.png");
TypedFile photoTypedFile = new TypedFile("image/*", photoFile);
retrofitAdapter.uploadFile(author, photoTypedFile)
.subscribe(<...>);
Ce qui crée une sortie similaire:
POST http://localhost:8000/api/v1/articles/ HTTP/1.1
Content-Type: multipart/form-data; boundary=32230279-83af-4480-abfc-88a880b21b19
Content-Length: 709
Host: localhost
Connection: Keep-Alive
Accept-Encoding: gzip
User-Agent: okhttp/2.3.0
--32230279-83af-4480-abfc-88a880b21b19
Content-Disposition: form-data; name="author"
Content-Type: text/plain; charset=UTF-8
Content-Length: 4
Content-Transfer-Encoding: binary
cUrl
--32230279-83af-4480-abfc-88a880b21b19
Content-Disposition: form-data; name="photo"; filename="article-photo.png"
Content-Type: image/*
Content-Length: 254
Content-Transfer-Encoding: binary
<!RAW BYTES HERE!>
--32230279-83af-4480-abfc-88a880b21b19--
La principale différence ici est que vous avez utilisé POJO Article article
comme paramètre multipartie, qui par défaut est converti par Converter
en json. Et votre serveur attend à la place une chaîne simple. Avec curl, vous envoyez cURL
, pas {"author":"cURL"}
.
Le serveur attend une chaîne "auteur" mais vous essayez de lui passer un objet "article". Passez-le "String author" au lieu de "Article article".
En outre, je pense que l'erreur "aucun fichier soumis" est un redingue car le fichier est clairement présent dans votre "request.data".