Post byte array to Web API server using HttpClient
Solution 1
WebAPI v2.1 and beyond supports BSON (Binary JSON) out of the box, and even has a MediaTypeFormatter
included for it. This means you can post your entire message in binary format.
If you want to use it, you'll need to set it in WebApiConfig
:
public static class WebApiConfig
{
public static void Register(HttpConfiguration config)
{
config.Formatters.Add(new BsonMediaTypeFormatter());
}
}
Now, you an use the same BsonMediaTypeFormatter
at the client side to serialize your request:
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();
}
Or, you can use Json.NET to serialize your class to BSON. Then, specify you want to use "application/bson" as your "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();
}
}
Solution 2
I convert Byte Array
into Base64 String
to post:
await client.PostAsJsonAsync( apiUrl,
new {
message = "",
content = Convert.ToBase64String(yourByteArray),
}
);
and receiver can convert the Base64 String
back to Byte Array
by:
string base64Str = (string)postBody.content;
byte[] fileBytes = Convert.FromBase64String(base64Str);
Solution 3
I have created this generic and cross platform method to support the BSON format using the Json.NET library so we can reuse it easier later. It works fine in Xamarin platform as well.
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;
}
}
The method to help to serialise data to BSON format:
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();
}
}
Then you can use the Post method like this:
var response = await PostBsonAsync<SamplePostRequest>("api/SomeData/Incoming", requestData);
Dennis
C# developer. Nowadays I mostly design/develop enterprise-level services and REST/SOAP APIs. From time to time still use XAML + MVVM.
Updated on October 30, 2020Comments
-
Dennis over 3 years
I want to post this data to Web API server:
public sealed class SomePostRequest { public int Id { get; set; } public byte[] Content { get; set; } }
Using this code for server:
[Route("Incoming")] [ValidateModel] public async Task<IHttpActionResult> PostIncomingData(SomePostRequest requestData) { // POST logic here }
and this - for 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();
everything works fine (at least, debugger stops at breakpoint in
PostIncomingData
).Since there is a
byte
array, I don't want to serialize it as JSON, and want to post it as binary data to decrease network traffic (something likeapplication/octet-stream
).How this can be achieved?
I've tried to play with
MultipartFormDataContent
, but looks like I just can't understand, howMultipartFormDataContent
will match signature of controller's method.E.g., replacing content to this:
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();
leads to error 415 ("Unsupported media type").
-
Dennis over 8 yearsThanks, it works perfectly. Actually, the link you've provided contains more simple example for the client side.
-
Yuval Itzchakov over 8 years@Dennis It uses the
BsonMediaTypeFormatter
to serialize. I guess you can have it either way. I'll add another example to my answer. -
Minh Nguyen about 8 yearsYour second code sample missing content type:
var byteArrayContent = new ByteArrayContent(stream.ToArray()); byteArrayContent.Headers.ContentType = new MediaTypeHeaderValue("application/bson"); var result = await client.PostAsync( "api/SomeData/Incoming", byteArrayContent);
-
Juan Zamora over 6 yearsreturn type for PostBson must be Task, Task<T> or void
-
Minh Nguyen over 6 years@JuanZamora you are right I just updated the code with await keyword
-
Simon over 4 yearsSimple and effective
-
Ray Cheng over 4 yearsI would say it's simple but not effective since the base64 string will be about 30% larger. Nevertheless, a working quick solution.
-
user1034912 over 2 yearsHow to consume??
-
Eike over 2 years@RayCheng I'd say it's simple and effective but not efficient.
-
John Lord over 2 yearsit also assumes you have control over the api, which you probably don't
-
Zi Cold over 2 yearsHow should reciever method look like? Where postBody variable is taken from?
-
yu yang Jian over 2 yearsit may be like: stackoverflow.com/questions/37842231/…
-
Igor Mironenko about 2 yearsThis is good but HttpClient is intended to be instantiated once per application - this code will likely exhaust the number of sockets available under heavy loads - see Microsoft docs - any version of .NET - docs.microsoft.com/en-us/dotnet/api/…
-
Igor Mironenko about 2 yearsHow are you consuming this in the controller...? using your 2nd example there, I get
UnsupportedMediaType
exception on the server - .Net5 web api