使用 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仅供参考,用于 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 服务器的主要内容,如果未能解决你的问题,请参考以下文章
使用 .NET 4.0 任务模式使用 HTTPClient .ReadAsAsync 将 JSON 反序列化为数组或列表