NancyFx / Nancy

Lightweight, low-ceremony, framework for building HTTP based services on .Net and Mono
http://nancyfx.org
MIT License
7.15k stars 1.47k forks source link

multipart form-data error #2940

Closed DGuidi closed 5 years ago

DGuidi commented 5 years ago

Prerequisites

Description

I'm trying to handle a multipart/form-data request made with postman using nancy 2.0 clinteastwood and .net core 2.1. I'm writing this because I've moved to netcore a code that worked in netfw 4.7.1 and nancy 1.4, so to me this looks a modified behavior.

Anyway I've written a test like this, that uses nancy.testing:

[TestMethod]
public void license_import_should_work()
{
        IBrowser browser = CreateBrowser();
        IBrowserResponse response = browser.Post("/v1/testenv/myresource", with =>
        {
                with.HttpRequest();
                with.BasicAuthAndScope("user", "pw");
                with.Header("Content-Type", "multipart/form-data");
                Assembly assembly = GetType().Assembly;
                Stream stream = assembly.GetManifestResourceStream("myfile.txt");
                Assert.IsNotNull(stream);
                BrowserContextMultipartFormData multipart = new BrowserContextMultipartFormData(x =>
                {
                        x.AddFile("file", "myfile.txt", "text/plain", stream);
                        x.AddFormField("aparam", null, Boolean.TrueString);
                });
                with.MultiPartFormData(multipart);
        });
        Assert.AreEqual(HttpStatusCode.OK, response.StatusCode);
        Assert.AreEqual("application/json; charset=utf-8", response.ContentType);
}

and this code works as expected.

This is my code that handles the request:

MediaRange contentType = Request.Headers.ContentType;
string boundary;
if (!MultipartHelper.IsMultiPart(contentType, out boundary))
        throw new BadRequestException();

HttpMultipart multipart = new HttpMultipart(Request.Body, boundary);
IEnumerable<HttpMultipartBoundary> boundaries = multipart
        .GetBoundaries()
        .ToList();
if (!boundaries.Any())
        throw new BadRequestException();

HttpMultipartBoundary multipartBoundary = boundaries
        .FirstOrDefault(b => String.Equals("text/plain", b.ContentType, Keys.StrComp));
if (multipartBoundary == null)
        throw new BadRequestException(err);
Stream stream = multipartBoundary.Value;
// ...code to handle stream from request

Code for MultipartHelper is taken from this issue but I've tested similar code found in blogposts with same results.

Then, I make the same request using postman and using my actual webapp, and basically the same code that works using unittest now throws a BadRequestException because boundaries (from my code) is an empty collection.

See also attached fiddler requests made from: postman browser via fetch

DGuidi commented 5 years ago

ok, I've made a super-simple test Multipart.zip and all looks ok, so I need to check somehow in my code...

DGuidi commented 5 years ago

ok, for anyone interested I've found the problem. basically I logged all requests using an hook in pipelines.BeforeRequest.AddItemToStartOfPipeline(cx => with a code that in nancy2 uses RequestStream body = RequestStream.FromStream(request.Body);, a change respect to nancy1.4 RequestStream body = request.Body;

In some cases I need to reset the stream using stream.Seek(0. SeekOrigin.Begin) to let the stream valid for subsequent processes, but RequestStream.Seek looks not reset stream. Using request.Body.Seek(0, SeekOrigin.Begin) fixes the behavior