【问题标题】:Post byte array to Web API server using HttpClient使用 HttpClient 将字节数组发布到 Web API 服务器
【发布时间】: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(“不支持的媒体类型”)。

【问题讨论】:

    标签: c# asp.net-web-api asp.net-web-api2 dotnet-httpclient


    【解决方案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);
    

    【讨论】:

    • 怎么消费??
    猜你喜欢
    • 1970-01-01
    • 2018-08-10
    • 1970-01-01
    • 2016-10-11
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-07-28
    相关资源
    最近更新 更多