3 votes

HttpClient téléchargeant MultipartFormData vers le framework play 2

J'ai le code suivant dans un projet Windows Phone 8 qui utilise le client RestSharp :

public async Task<string> DoMultiPartPostRequest(String ext, JSonWriter jsonObject, ObservableCollection<Attachment> attachments)
    {
        var client = new RestClient(DefaultUri);
        // client.Authenticator = new HttpBasicAuthenticator(username, password);

        var request = new RestRequest(ext, Method.POST);

        request.RequestFormat = DataFormat.Json;
        request.AddParameter("json", jsonObject.ToString(), ParameterType.GetOrPost);

        // add files to upload
        foreach (var a in attachments)
            request.AddFile("attachment", a.FileBody, "attachment.file", a.ContType);

        var content = await client.GetResponseAsync(request);

        if (content.StatusCode != HttpStatusCode.OK)
            return "error";

        return content.Content;
    }

Fiddler montre l'en-tête généré :

POST http://192.168.1.101:9000/rayz/create HTTP/1.1
Content-Type: multipart/form-data; boundary=-----------------------------28947758029299
Content-Length: 71643
Accept-Encoding: identity
Accept: application/json, application/xml, text/json, text/x-json, text/javascript, text/xml
User-Agent: RestSharp 104.1.0.0
Host: 192.168.1.101:9000
Connection: Keep-Alive
Pragma: no-cache

-------------------------------28947758029299
Content-Disposition: form-data; name="json"

{
    "userId": "2D73B43390041E868694A85A65E47A09D50F019C180E93BAACC454488F67A411",
    "latitude": "35.09",
    "longitude": "33.30",
    "accuracy": "99",
    "maxDistance": "dist",
    "Message": "mooohv"
}
-------------------------------28947758029299
Content-Disposition: form-data; name="attachment"; filename="attachment.file"
Content-Type: image/jpeg

?????JFIF??`?`?????C?  $" &0P40,,0bFJ:Ptfzxrfpn????????np????????|????????????C"$$0*0^44^?p??????????????????????????????????????????????????????`?"??????????????
-------------------------------28947758029299

Le code ci-dessus fonctionne bien sur l'API Play2. Cependant, comme RestSharp ne semble pas être stable, j'ai décidé d'utiliser le HttpClient natif fourni par Microsoft.

J'ai donc écrit une autre fonction qui utilise HttpClient pour faire le même travail :

public async Task<string> DoMultiPartPostRequest2(String ext, JSonWriter jsonObject,
                                                                 ObservableCollection<Attachment> attachments)
    {
        var client = new HttpClient();

        var content = new MultipartFormDataContent();

        var json = new StringContent(jsonObject.ToString());
        content.Add(json, "json");

        foreach (var a in attachments)
        {
            var fileContent = new StreamContent(new MemoryStream(a.FileBody));
            fileContent.Headers.ContentDisposition = new ContentDispositionHeaderValue("form-data")
            {
                Name = "attachment",
                FileName = "attachment.file"
            };
            fileContent.Headers.ContentType = MediaTypeHeaderValue.Parse(a.ContType);
            content.Add(fileContent);
        }

        var resp = await client.PostAsync(DefaultUri + ext, content);

        if (resp.StatusCode != HttpStatusCode.OK)
            return "error";

        var reponse = await resp.Content.ReadAsStringAsync();

        return reponse;
    }

L'en-tête généré par ce code est le suivant :

POST http://192.168.1.101:9000/rayz/create HTTP/1.1
Accept: */*
Content-Length: 6633
Accept-Encoding: identity
Content-Type: multipart/form-data; boundary="e01b2196-d24a-47a2-a99b-e82cc4a2f92e"
User-Agent: NativeHost
Host: 192.168.1.101:9000
Connection: Keep-Alive
Pragma: no-cache

--e01b2196-d24a-47a2-a99b-e82cc4a2f92e
Content-Type: text/plain; charset=utf-8
Content-Disposition: form-data; name=json

{
    "userId": "2D73B43390041E868694A85A65E47A09D50F019C180E93BAACC454488F67A411",
    "latitude": "35.09",
    "longitude": "33.30",
    "accuracy": "99",
    "maxDistance": "dist",
    "Message": "test"
}
--e01b2196-d24a-47a2-a99b-e82cc4a2f92e
Content-Disposition: form-data; name=attachment; filename=attachment.file
Content-Type: image/jpeg

?????JFIF??`?`?????C?  $" &0P40,,0bFJ:Ptfzxrfpn????????np????????|????????????C"$$0*0^44^?p????????????????????????????????????????????????????????"??????????????
--e01b2196-d24a-47a2-a99b-e82cc4a2f92e--

Jusqu'à présent, tout va bien. De mon point de vue, les deux en-têtes semblent être identiques.

Cependant, lorsque je débogue l'API de Play 2 après avoir exécuté Http.MultipartFormData body = request().body().asMultipartFormData(); J'ai remarqué que les données multipart ne sont pas analysées correctement.

Plus précisément, le fichier multipart dans la variable body est le suivant :

MultipartFormData(Map(),List(),List(BadPart(Map(ntent-type -> text/plain; charset=utf-8, content-disposition -> form-data; name=json)), BadPart(Map()), BadPart(Map()), BadPart(Map()), BadPart(Map())),List())

Comme vous pouvez le constater, il comporte plusieurs (en fait 5 dans cet exemple) BadParts. Exemple : BadPart(Map(ntent-type -> text/plain; charset=utf-8, content-disposition -> form-data; name=json))

Quelqu'un peut-il voir ce qui ne va pas ici ? L'en-tête généré par HttpClient est-il incorrect ?

4voto

George Nikolaides Points 963

Voici la solution (hack)

Il semble y avoir un problème avec Play Framework lorsque la frontière comporte des guillemets.

J'ai donc ajouté le code suivant après la création de la multipart afin de les supprimer :

var content = new MultipartFormDataContent();

foreach (var param in content.Headers.ContentType.Parameters.Where(param => param.Name.Equals("boundary")))
     param.Value = param.Value.Replace("\"", String.Empty);

Finalement, j'ai dû ajouter manuellement des guillemets "\"" à des valeurs spécifiques de l'en-tête, comme suit :

Original : Content-Disposition: form-data; name=attachment; filename=attachment.file Changé en : Content-Disposition: form-data; name="attachment"; filename="attachment.file"

et

Original : Content-Disposition: form-data; name=json Changé en : Content-Disposition: form-data; name="json"

Je ne pense pas que ce soit une erreur d'avoir des guillemets ou non n'importe où dans l'en-tête et peut-être que le parsing sur le play framework devrait être corrigé en conséquence.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X