Poster un tableau d'octets sur un serveur D'API Web à L'aide de HttpClient

Je veux publier ces données sur le serveur Web API:

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

Utilisation de 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).

Comme il existe un tableau byte, Je ne veux pas le sérialiser en tant que JSON et je veux le publier en tant que données binaires pour diminuer le trafic réseau (quelque chose comme application/octet-stream).

Comment cela peut-il être réalisé?

J'ai essayé de jouer avec MultipartFormDataContent, mais semble comme je ne peux tout simplement pas comprendre, comment MultipartFormDataContent correspondra à 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").

24
demandé sur Yuval Itzchakov 2015-08-24 17:06:28

4 réponses

WebAPI v2.1 et au-delà prend en charge BSON (JSON binaire) hors de la boîte , et a même un MediaTypeFormatter inclus pour cela. Cela signifie que vous pouvez poster votre message entier en 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 utilisez le 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 souhaitez utiliser "application / bson" comme votre "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();
    }
}
30
répondu Yuval Itzchakov 2017-01-24 09:25:27

J'ai créé cette méthode générique et multi-plateforme pour prendre en charge le format BSON en utilisant le Json.NET bibliothèque afin que nous puissions le réutiliser plus facilement plus tard. Cela fonctionne aussi bien dans 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
répondu Minh Nguyen 2017-12-22 08:59:35

Fyi, pour la sérialisation 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
répondu iowatiger08 2017-08-17 16:12:43

Je convertit Byte Array en Base64 String pour poster:

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

Et récepteur peut convertir le byte array Retour à Base64 string par:

string base64Str = (string)postBody.data;
byte[] fileBytes = Convert.FromBase64String(base64Str);
0
répondu yu yang Jian 2018-10-02 09:01:36