Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

.NET Core HttpClient upload byte array gives unsupported media type error

I'm trying to upload a simple byte array for my Web Api controller (ASP.NET Core 3)

using var client = new HttpClient() { BaseAddress = new Uri("http://someUrl.com/") };
var body = new ByteArrayContent(new byte[] {1, 2, 3});

var result = await client.PostAsync("api/somecontroller/content?someField=someData", body);

Controller

[HttpPost("content")]
public IActionResult Upload([FromBody]byte[] documentData, [FromQuery] string someField)
{
    ...

    return Ok();
}

but this gives me the error 415 Unsupported media type. Why ? I need to put some additional data in the url, but I don't think that's the issue here.

like image 563
Tony Avatar asked Jun 16 '20 19:06

Tony


4 Answers

While byte[] would be a great way to represent application/octet-stream data, this is not the case by default in asp.net core Web API.

Here is a simple workaround:

Send request by HttpClient:

using var client = new HttpClient() { BaseAddress = new Uri("http://localhost:62033") };
var body = new ByteArrayContent(new byte[] { 1, 2, 3 });
body.Headers.ContentType = MediaTypeHeaderValue.Parse("application/octet-stream");           
var result = await client.PostAsync("api/Values/content?someField=someData", body);

Receive action in Web Api project:

[HttpPost("content")]
public IActionResult Upload([FromBody]byte[] documentData, [FromQuery] string someField)
{
        return Ok();
}

Custom InputFormatter in Web Api Project:

public class ByteArrayInputFormatter : InputFormatter
{
    public ByteArrayInputFormatter()
    {
        SupportedMediaTypes.Add(Microsoft.Net.Http.Headers.MediaTypeHeaderValue.Parse("application/octet-stream"));
    }

    protected override bool CanReadType(Type type)
    {
        return type == typeof(byte[]);
    }

    public override Task<InputFormatterResult> ReadRequestBodyAsync(InputFormatterContext context)
    {
        var stream = new MemoryStream();
        await context.HttpContext.Request.Body.CopyToAsync(stream);
        return InputFormatterResult.SuccessAsync(stream.ToArray());
    }
}

Startup.cs in Web Api Project:

services.AddControllers(options=> 
        options.InputFormatters.Add(new ByteArrayInputFormatter()));

Result:

enter image description here

like image 67
Rena Avatar answered Nov 14 '22 23:11

Rena


The issue is caused by the [FromBody] attribute which supports only simple types. You can use Type Converters for other types. The correct controller action code should be:

    [HttpPost("content")]
    public IActionResult content([FromQuery] string someField)
    {
        var documentData= new byte[Request.ContentLength.Value];
         Request.Body.ReadAsync(documentData);
        //...
        return Ok();
    }
like image 45
EricN Avatar answered Nov 15 '22 00:11

EricN


Be aware that the answer above has typos, which may cost you quite a lot of time (code in the gif file is incorrect and may produce damaged not complete byte arrays):

public async override Task<InputFormatterResult> ReadRequestBodyAsync(InputFormatterContext context)
    {
        var stream = new MemoryStream();
        await context.HttpContext.Request.Body.CopyToAsync(stream);
        return InputFormatterResult.Success(stream.ToArray());
    }
like image 2
Access Denied Avatar answered Nov 14 '22 22:11

Access Denied


Based on the excellent answer from @Rena you can also use the following class to read the request body as a Stream parameter:

public class StreamInputFormatter: InputFormatter
{
  public StreamInputFormatter()
    => SupportedMediaTypes.Add(MediaTypeHeaderValue.Parse("application/octet-stream"));

  protected override bool CanReadType(Type ArgType) => ArgType == typeof(Stream);

  public override Task<InputFormatterResult> ReadRequestBodyAsync(InputFormatterContext ArgContext)
    => InputFormatterResult.SuccessAsync(ArgContext.HttpContext.Request.Body);
}

Your API controller action can then look something like this:

  [HttpPost]
  [Consumes("application/octet-stream")]
  public async Task<ActionResult> UploadData([FromBody] Stream ArgData) {}

When you do the above and use Swagger UI via the Swashbuckle.AspNetCore package, it will correctly show a file-input control which allows you to upload the binary data:

enter image description here

like image 1
Eric Mutta Avatar answered Nov 14 '22 23:11

Eric Mutta