4

I am working on a service that will receive a file using the HTTP Content-Type: application/octet-stream and Transfer-Encoding: chunked.

I was able to get the server to get the request DisableFormValueModelBinding and RequestSizeLimit.

But when I go to get the data from the Request.Body the length is always 0.

In Framework I would use something like request.Content.ReadAsStreamAsync(); But this doesn't seem to be an option for Core.

How am I supposed get the content of the stream coming from the client (Postman)?

Using Postman I tried the binary and form-data options for the body but neither ended up having a body once it reached the server. Reading through some of the documentation there was suggestions around creating a new formatter that used a MultipartReader. But this all looked to be based around having the multipart/form-data content-type, which I am not using. I also tried using curl to send the request but the results were the same.

Program.cs

public class Program
{
    public static void Main(string[] args)
    {
        CreateWebHostBuilder(args).Build().Run();
    }

    public static IWebHostBuilder CreateWebHostBuilder(string[] args) =>
        WebHost.CreateDefaultBuilder(args)
            .UseStartup<Startup>();
}

Startup.cs

public class Startup
{
    public Startup(IConfiguration configuration)
    {
        Configuration = configuration;
    }

    public IConfiguration Configuration { get; }

    // This method gets called by the runtime. Use this method to add services to the container.
    public void ConfigureServices(IServiceCollection services)
    {
        services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_2);
    }

    // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
    public void Configure(IApplicationBuilder app, IHostingEnvironment env)
    {
        if (env.IsDevelopment())
        {
            app.UseDeveloperExceptionPage();
        }
        else
        {
            // The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts.
            app.UseHsts();
        }

        app.UseHttpsRedirection();
        app.UseMvc();
    }
}

Controller

[HttpPost]
[RequestSizeLimit(2147483648)] // https://stackoverflow.com/questions/43305220/form-key-or-value-length-limit-2048-exceeded
[DisableFormValueModelBinding] // https://dotnetcoretutorials.com/2017/03/12/uploading-files-asp-net-core/
public void Upload()
{
    Request.EnableRewind();
    Stream body = Request.Body;
    Debug.WriteLine(body.Length);
}
165plo
  • 713
  • 1
  • 8
  • 20
  • 1
    Did you specify a "Body" in Postman? – Gabriel Luci Dec 20 '18 at 00:12
  • @GabrielLuci I did. I tried the binary and form-data options but neither ended up having a body. Reading through some of the documentation there was suggestions around creating a new formatter that used a MultipartReader. But this all looked to be based around having the multipart/form-data content-type, which I am not using. – 165plo Dec 20 '18 at 00:29
  • @GabrielLuci unless I miss read the doc, StreamFile is a helper method that they created to work with the multipart data. Unless I am misunderstanding what the MultipartReader supports this won't work for my situation. – 165plo Dec 20 '18 at 00:45
  • Right. That makes sense. But I think I see why it's 0. See my answer. – Gabriel Luci Dec 20 '18 at 00:57

1 Answers1

8

I read that article where you got the [DisableFormValueModelBinding] from. The whole point of that attribute is to stop ASP.NET from reading the body. So that would make sense why body.Length is 0. It simply hasn't been read yet (and you can't know the length until you've read the whole thing).

But you can read the Content-Length header of the request:

var length = Request.ContentLength;
Gabriel Luci
  • 38,328
  • 4
  • 55
  • 84
  • I dont agree. The attribute stops the other formatters from reading the body. Ideally if the body has been left alone it should be avalible for access. Maybe if I make a custom formatter I will have access to it? – 165plo Dec 20 '18 at 01:41
  • 1
    I'm not saying that it stops *you* from reading it. I'm saying that *you haven't read it yet*. The whole point of a stream is that you can process stuff as it comes in without loading the entire thing into memory. So by its very definition, you can't know how much data there is until you've read it to the end, which you haven't done yet - or unless something else tells you: and that's what the `Content-Length` header is for. The client tells you in that header, "this is how much data I'm sending". – Gabriel Luci Dec 20 '18 at 01:49
  • Ok so by that thought I should be able to do a read on the Body even though the length and position are 0? I can give it a try. Though the length is supposed to return how many bytes are in the stream. – 165plo Dec 20 '18 at 01:54
  • Based on an initial test you are correct. I was able to read a byte out of the stream despite the length being 0. I will update when I get the file passed and back to its original form. – 165plo Dec 20 '18 at 02:02
  • So if there is data in the stream why isnt there a length? My thought was that the length would just be growing until the whole file was received. And the stream would manage where the data that isnt in memory goes. For example a FileStreams length is the length of the file. – 165plo Dec 20 '18 at 02:39
  • The documentation for `Stream.Length` doesn't say much, but it makes sense that it would work for a `FileStream` and not an `HttpRequest`. The size of a file is available in the file system without reading the whole file. Even [@JonSkeet says](http://jonskeet.uk/csharp/readbinary.html): "While you could use Stream.Length, it isn't supported for all streams." – Gabriel Luci Dec 20 '18 at 13:53
  • Which version of .NET Core are you using? I tried this in a 2.0 project and `Request.Body.Length` throws a `NotSupportedException` – Gabriel Luci Dec 20 '18 at 13:54
  • I had not seen that post from @JonSkeet. Thanks for that reference. The Request.EnableRewind resolves the NotSupportedException. This was something I read early on. But since I don't have to seek in the body I think I don't need that anymore. Thank you for helping with this – 165plo Dec 20 '18 at 16:16
  • Ah, that makes sense (re: EnableRewind). – Gabriel Luci Dec 20 '18 at 16:59