Darren Wainwright
Darren Wainwright

Reputation: 30737

HttpRequestMessage Content Disposition null when unit testing

I am trying to write tests for a custom MultipartMemoryStreamProvider - one that is very similar to this MultipartFormDataMemoryStreamProvider.cs

In particular, I am trying to test my own implementation of the GetStream(HttpContent parent, HttpContentHeaders headers) method.

It requires an HttpContent and HttpContentHeaders.

To achieve this I am trying to create a controller context and controller, then pass through the appropriate properties from that controllers request.

In fact, I have tried to implement the answer on this (duplicate) question: Testing a Web API method that uses HttpContext.Current.Request.Files?

Everything I try results in the Content-Disposition on the headers being null As shown in the images below:

enter image description here

enter image description here

Any idea what I am missing?

For code-sake, here is a copy of the code. You will notice it's the same as that in the answer on the other question. I just can't get passed the null content-disposition.

  var content = new ByteArrayContent(new Byte[100]);
        content.Headers.Add("Content-Disposition", "form-data");
        var controllerContext = new HttpControllerContext
        {
            Request = new HttpRequestMessage
            {
                Content = new MultipartContent { content }
            }
        };
        var controller = new MockController();
        controller.ControllerContext = controllerContext;

MockController is simply:

public class MockController : ApiController { }

Upvotes: 4

Views: 2978

Answers (2)

Andrii Litvinov
Andrii Litvinov

Reputation: 13182

If you expect Content-Disposition header to appear in MultipartContent you should set in on MultipartContent not on ByteArrayContent.

But if you want to emulate file upload form a web page you do need to set Content-Disposition header on ByteArrayContent. In that case to see the value of Content-Disposition you need to iterate through inner contents of MultipartContent:

var multipart = await Request.Content.ReadAsMultipartAsync();
foreach (var content in result.Contents)
{
     var contentDisposition = content.Headers.ContentDisposition;
}

You can check this tutorial: Sending HTML Form Data in ASP.NET Web API. It might be a bit outdated but it shows the idea.

EDIT: Just checked and it appears to be same tutorial mentioned in the answer you referenced in your question.

Upvotes: 2

Dávid Molnár
Dávid Molnár

Reputation: 11573

Please correct me if I'm wrong. Here's my analysis of the situation:

The class MultipartContent basically has a collection of child HttpContent objects (ByteArrayContent is inheriting from HttpContent). It overrides the WriteNextContentHeadersAsync method and writes out the headers from its child HttpContent collection. It does have its own Headers property, but it is inherited from HttpContent, no overrides, nothing special.

So, what you are trying to do, is basically not implemented in that way. The MultipartContent does write out the headers to the output, but does not reflect these headers in its own Headers collection.

You could override this behavior. Simple and naive implementation could be the following:

public class MyMultipartContent : MultipartContent
{
    public override void Add(HttpContent content)
    {
        base.Add(content);
        foreach (var header in content.Headers.ToList())
        {
            if (!this.Headers.Contains(header.Key))
                this.Headers.Add(header.Key, header.Value);
        }
    }
}

So every header from a child HttpContent will be added to the MultipartContent's Header collection. Maybe this is completely wrong and will cause errors and other problems :)

Another option would be to enumerate child HttpContent objects in the MultipartContent:

foreach (var httpContent in (MultipartContent) controller.Request.Content)
{
    var header = httpContent.Headers.ContentDisposition; // here it is
}

Another option is to use an extension method:

public static class MyExtensions
{
    public static ContentDispositionHeaderValue MyGetContentDisposition(this HttpContent httpContent)
    {
        if (httpContent is MultipartContent)
            return ((MultipartContent) httpContent).FirstOrDefault(c => c.Headers.ContentDisposition != null)?.Headers.ContentDisposition;
        return httpContent.Headers.ContentDisposition;
    }
}

If the httpContent is MultipartContent then return the first not null ContentDisposition header from it's child HttpContent collection.

var hereItIs = controller.Request.Content.MyGetContentDisposition();

Upvotes: 0

Related Questions