使用 HttpClient 将字节数组发布到 Web API 服务器

Posted

技术标签:

【中文标题】使用 HttpClient 将字节数组发布到 Web API 服务器【英文标题】:Post byte array to Web API server using HttpClient 【发布时间】:2015-11-17 23:57:24 【问题描述】:

我想将此数据发布到 Web API 服务器:

public sealed class SomePostRequest

    public int Id  get; set; 
    public byte[] Content  get; set; 

将此代码用于服务器:

[Route("Incoming")]
[ValidateModel]
public async Task<IHttpActionResult> PostIncomingData(SomePostRequest requestData)

    // POST logic here

这个 - 给客户:

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();

一切正常(至少,调试器在PostIncomingData 的断点处停止)。

由于有一个byte 数组,我不想将其序列化为 JSON,而是希望将其作为二进制数据发布以减少网络流量(类似于application/octet-stream)。

如何做到这一点?

我尝试过使用MultipartFormDataContent,但似乎我无法理解MultipartFormDataContent 将如何匹配控制器方法的签名。

例如,将内容替换为:

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();

导致错误 415(“不支持的媒体类型”)。

【问题讨论】:

【参考方案1】:

WebAPI v2.1 及更高版本supports BSON (Binary JSON) out of the box,甚至还包含一个MediaTypeFormatter。这意味着您可以以二进制格式发布整个消息。

如果你想使用它,你需要在WebApiConfig中设置它:

public static class WebApiConfig

    public static void Register(HttpConfiguration config)
    
        config.Formatters.Add(new BsonMediaTypeFormatter());
    

现在,您可以在客户端使用相同的 BsonMediaTypeFormatter 来序列化您的请求:

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();

或者,您可以使用 Json.NET 将您的类序列化为 BSON。然后,指定要使用“application/bson”作为“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();
    

【讨论】:

谢谢,它工作得很好。实际上,您提供的链接包含更简单的客户端示例。 @Dennis 它使用BsonMediaTypeFormatter 进行序列化。我想你可以有它任何一种方式。我将在我的答案中添加另一个示例。 您的第二个代码示例缺少内容类型:var byteArrayContent = new ByteArrayContent(stream.ToArray()); byteArrayContent.Headers.ContentType = new MediaTypeHeaderValue("application/bson"); var result = await client.PostAsync( "api/SomeData/Incoming", byteArrayContent);【参考方案2】:

我将Byte Array 转换为Base64 String 来发帖:

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

接收者可以通过以下方式将Base64 String 转换回Byte Array

string base64Str = (string)postBody.content;
byte[] fileBytes = Convert.FromBase64String(base64Str);

【讨论】:

简单有效 我会说这很简单但效果不佳,因为 base64 字符串会大 30% 左右。不过,这是一个有效的快速解决方案。 @RayCheng 我会说它简单有效但效率不高。 它还假设您可以控制 api,但您可能没有 reciever 方法应该是什么样子? postBody 变量取自哪里?【参考方案3】:

我使用 Json.NET 库创建了这个通用的跨平台方法来支持 BSON 格式,以便我们以后可以更轻松地重用它。它在 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;
    

帮助将数据序列化为 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();
    

然后你可以像这样使用 Post 方法:

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

【讨论】:

PostBson 的返回类型必须是 Task、Task 或 void @JuanZamora 你说得对,我刚刚用 await 关键字更新了代码【参考方案4】:

仅供参考,用于 protobuf 序列化以请求正文帖子

        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);

【讨论】:

怎么消费??

以上是关于使用 HttpClient 将字节数组发布到 Web API 服务器的主要内容,如果未能解决你的问题,请参考以下文章

将 HttpClient 的响应映射到对象数组

使用 VB.Net 将两个字节数组附加到一个字节数组

使用 JavaFX 将 BMP 加载到字节数组中

使用 .NET 4.0 任务模式使用 HTTPClient .ReadAsAsync 将 JSON 反序列化为数组或列表

将范围为 -128 到 127 的字节数组转换为字符串数组

将图像读取到字节数组并更改它