web-dev-qa-db-fra.com

Publiez un tableau d'octets sur le serveur d'API Web à l'aide de HttpClient

Je souhaite publier ces données sur un serveur API Web:

public sealed class SomePostRequest
{
    public int Id { get; set; }
    public byte[] Content { get; set; }
}

En utilisant ce code pour le serveur:

[Route("Incoming")]
[ValidateModel]
public async Task<IHttpActionResult> PostIncomingData(SomePostRequest requestData)
{
    // POST logic here
}

et ceci - pour le client:

var client = new HttpClient();
client.BaseAddress = new Uri("http://localhost:25001/");
client.DefaultRequestHeaders.Accept.Clear();
client.DefaultRequestHeaders.Accept.Add(
    new MediaTypeWithQualityHeaderValue("application/json"));

var content = new FormUrlEncodedContent(new Dictionary<string, string>
{
    { "id", "1" },
    { "content", "123" }
});

var result = await client.PostAsync("api/SomeData/Incoming", content);
result.EnsureSuccessStatusCode();

tout fonctionne bien (au moins, le débogueur s'arrête au point d'arrêt dans PostIncomingData). 

Dans la mesure où il existe un tableau byte, je ne souhaite pas le sérialiser en tant que JSON ni le publier en tant que données binaires pour réduire le trafic réseau (quelque chose comme application/octet-stream).

Comment cela peut être réalisé? 

J'ai essayé de jouer avec MultipartFormDataContent, mais il semble que je ne puisse tout simplement pas comprendre comment MultipartFormDataContent correspond à la signature de la méthode du contrôleur.

Par exemple, en remplaçant le contenu par ceci:

var content = new MultipartFormDataContent();
content.Add(new FormUrlEncodedContent(new Dictionary<string, string> { { "id", "1" } }));

var binaryContent = new ByteArrayContent(new byte[] { 1, 2, 3 });
binaryContent.Headers.ContentType = new MediaTypeHeaderValue("application/octet-stream");
content.Add(binaryContent, "content");

var result = await client.PostAsync("api/SomeData/Incoming", content);
result.EnsureSuccessStatusCode();

conduit à l'erreur 415 ("Type de support non pris en charge").

25
Dennis

WebAPI v2.1 et au-delà prend en charge BSON (JSON binaire) prêt à l'emploi , et a même une MediaTypeFormatter incluse. Cela signifie que vous pouvez poster l'intégralité de votre message au format binaire.

Si vous voulez l'utiliser, vous devrez le définir dans WebApiConfig:

public static class WebApiConfig
{
    public static void Register(HttpConfiguration config)
    {
        config.Formatters.Add(new BsonMediaTypeFormatter());
    }
}

Maintenant, vous pouvez utiliser la même BsonMediaTypeFormatter du côté client pour sérialiser votre demande:

public async Task SendRequestAsync()
{
    var client = new HttpClient
    {
        BaseAddress = new Uri("http://www.yourserviceaddress.com");
    };

    // Set the Accept header for BSON.
    client.DefaultRequestHeaders.Accept.Clear();
    client.DefaultRequestHeaders.Accept.Add(
            new MediaTypeWithQualityHeaderValue("application/bson"));

    var request = new SomePostRequest
    {
        Id = 20,
        Content = new byte[] { 2, 5, 7, 10 }
    };

    // POST using the BSON formatter.
    MediaTypeFormatter bsonFormatter = new BsonMediaTypeFormatter();
    var result = await client.PostAsync("api/SomeData/Incoming", request, bsonFormatter);

    result.EnsureSuccessStatusCode();
}

Ou, vous pouvez utiliser Json.NET pour sérialiser votre classe à BSON. Ensuite, spécifiez que vous voulez utiliser "application/bson" comme "Content-Type":

public async Task SendRequestAsync()
{   
    using (var stream = new MemoryStream())
    using (var bson = new BsonWriter(stream))
    {
        var jsonSerializer = new JsonSerializer();

        var request = new SomePostRequest
        {
            Id = 20,
            Content = new byte[] { 2, 5, 7, 10 }
        };

        jsonSerializer.Serialize(bson, request);

        var client = new HttpClient
        {
            BaseAddress = new Uri("http://www.yourservicelocation.com")
        };

        client.DefaultRequestHeaders.Accept.Clear();
        client.DefaultRequestHeaders.Accept.Add(
                new MediaTypeWithQualityHeaderValue("application/bson"));

        var byteArrayContent = new ByteArrayContent(stream.ToArray());
        byteArrayContent.Headers.ContentType = new MediaTypeHeaderValue("application/bson");

        var result = await client.PostAsync(
                "api/SomeData/Incoming", byteArrayContent);

        result.EnsureSuccessStatusCode();
    }
}
32
Yuval Itzchakov

J'ai créé cette méthode générique et multiplateforme pour prendre en charge le format BSON à l'aide de la bibliothèque Json.NET afin que nous puissions le réutiliser plus tard. Cela fonctionne aussi bien sur la plate-forme Xamarin.

public static async HttpResponseMessage PostBsonAsync<T>(string url, T data)
{
    using (var client = new HttpClient())
    {
        //Specifiy 'Accept' header As BSON: to ask server to return data as BSON format
        client.DefaultRequestHeaders.Accept.Clear();
        client.DefaultRequestHeaders.Accept.Add(
                new MediaTypeWithQualityHeaderValue("application/bson"));

        //Specify 'Content-Type' header: to tell server which format of the data will be posted
        //Post data will be as Bson format                
        var bSonData = HttpExtensions.SerializeBson<T>(data);
        var byteArrayContent = new ByteArrayContent(bSonData);
        byteArrayContent.Headers.ContentType = new MediaTypeHeaderValue("application/bson");

        var response = await client.PostAsync(url, byteArrayContent);

        response.EnsureSuccessStatusCode();

        return response;
    }
}

La méthode pour aider à sérialiser les données au format BSON:

public static byte[] SerializeBson<T>(T obj)
{
    using (MemoryStream ms = new MemoryStream())
    {
        using (BsonWriter writer = new BsonWriter(ms))
        {
            JsonSerializer serializer = new JsonSerializer();
            serializer.Serialize(writer, obj);
        }

        return ms.ToArray();
    }
}

Ensuite, vous pouvez utiliser la méthode Post comme ceci:

var response = await PostBsonAsync<SamplePostRequest>("api/SomeData/Incoming", requestData);
4
Minh Nguyen

Je convertis Byte Array en Base64 String pour poster: 

await client.PostAsJsonAsync( apiUrl,  
    new  {
        message = "",
        content = Convert.ToBase64String(yourByteArray),
    }
);

et le destinataire peut reconvertir le byte array en Base64 string par:

string base64Str = (string)postBody.data;
byte[] fileBytes = Convert.FromBase64String(base64Str);
0
yu yang Jian

Fyi, pour la sérialisation de protobuf pour demander des messages de corps

        LoginRequest loginRequest = new LoginRequest()
        {
            Code = "UserId",
            Password = "myPass",
            CMToken = "eIFt4lYTKGU:APA91bFZPe3XCDL2r1JUJuEQLlN3FoeFw9ULpw8ljEavNdo9Lc_-Qua4w9pTqdOFLTb92Kf03vyWBqkcvbBfYEno4NQIvp21kN9sldDt40eUOdy0NgMRXf2Asjp6FhOD1Kmubx1Hq7pc",
        };
        byte[] rawBytes = ProtoBufSerializer.ProtoSerialize<LoginRequest>(loginRequest);

        var client = new HttpClient();
        client.BaseAddress = new Uri("http://localhost:9000/");
        client.DefaultRequestHeaders.Accept.Clear();
        client.DefaultRequestHeaders.Accept.Add(
            new MediaTypeWithQualityHeaderValue("application/x-protobuf"));

        //var bSonData = HttpExtensions.SerializeBson<T>(data);
        var byteArrayContent = new ByteArrayContent(rawBytes);
        byteArrayContent.Headers.ContentType = new MediaTypeHeaderValue("application/x-protobuf");

        var result = client.PostAsync("Api/Login", byteArrayContent).Result;

        Console.WriteLine(result.IsSuccessStatusCode);
0
iowatiger08