Gavin Gavin - 2 months ago 10
C# Question

Reading HttpContent bytes fails inside DelegatingHandler when multiple content types present

I'm trying to implement HMAC security for an API. Everything works fine until I try to post data values alongside a file in a

MultipartFormDataContent
.

The
HttpClient
DelegatingHandler
fails silently when the async line of code to read bytes is hit.

Here's the code building the request:

private FileOutputViewModel GetApiOutput(Uri apiResource, string filename, byte[] file, IDictionary<string, string> extraParameters)
{
FileOutputViewModel result = new FileOutputViewModel();

if (file != null)
{
using (var content = new MultipartFormDataContent())
{
if (extraParameters != null)
{
foreach (var param in extraParameters)
{
content.Add(new StringContent(param.Value), param.Key); // <- If I don't have this, everything works fine
}
}

var fileContent = new ByteArrayContent(file);
fileContent.Headers.ContentDisposition = new ContentDispositionHeaderValue("attachment")
{
FileName = filename
};
content.Add(fileContent);

var response = HttpClient.PostAsync(apiResource.ToString(), content).Result;

result.Output = JsonConvert.DeserializeObject(response.Content.ReadAsStringAsync().Result);

result.Filename = Path.GetFileName(filename);
}
}

return result;
}


If I don't use the
DelegatingHandler
everything works fine, but the HMAC security isn't implemented for the request so is rejected on the API's end.

If I don't add the data values using
StringContent
items alongside the file then there's no problem reading the bytes. But I'm left with an incomplete request as I need to pass more info along with the file.

The line of code that fails in the
DelegatingHandler
is indicated below:

private static async Task<byte[]> ComputeHash(HttpContent httpContent)
{
using (var md5 = MD5.Create())
{
byte[] hash = null;
if (httpContent != null)
{
var ms = new MemoryStream();
await httpContent.CopyToAsync(ms); // <- Fails here
ms.Seek(0, SeekOrigin.Begin);

var content = ms.ToArray();
if (content.Length != 0)
{
hash = md5.ComputeHash(content);
}
}
return hash;
}
}


Originally the failing line was:

var content = await httpContent.ReadAsByteArrayAsync();


but this failed with even just the file on its own (previous Stackoverflow question). Using a
MemoryStream
was one step forward but hasn't got me all the way.

Any ideas how I might be able to work around this issue?

Answer

Seems this was caused by having an async signature for the System.Net.Http.DelegatingHandler.SendAsync method. Originally the delegate override was:

protected async override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)

when I adapted the code so I could change it to:

protected override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)

everything started to work as expected.