API File Upload using HTTP content not exposed in swagger

13,168

You'll need to add a custom IOperationFilter to handle this.

Given you have a controller like so:

    [ValidateMimeMultipartContentFilter]
    [HttpPost, Route("softwarepackage")]
    public Task<SoftwarePackageModel> UploadSingleFile()
    {

        var streamProvider = new MultipartFormDataStreamProvider(ServerUploadFolder);
        var task = Request.Content.ReadAsMultipartAsync(streamProvider).ContinueWith<SoftwarePackageModel>(t =>
        {
            var firstFile = streamProvider.FileData.FirstOrDefault();

            if (firstFile != null)
            {
                // Do something with firstFile.LocalFileName
            }

            return new SoftwarePackageModel
            {

            };
        });

        return task;
    }

You then need to create an Swashbuckle.Swagger.IOperationFilter to add a file upload parameter to your function like:

    public class FileOperationFilter : IOperationFilter
    {
        public void Apply(Operation operation, SchemaRegistry schemaRegistry, ApiDescription apiDescription)
        {
            if (operation.operationId.ToLower() == "softwarepackage_uploadsinglefile")
            {
                if (operation.parameters == null)
                    operation.parameters = new List<Parameter>(1);
                else
                    operation.parameters.Clear();
                operation.parameters.Add(new Parameter
                {
                    name = "File",
                    @in = "formData",
                    description = "Upload software package",
                    required = true,
                    type = "file"
                });
                operation.consumes.Add("application/form-data");
            }
        }
    }

And in your Swagger config you'll need to register the filter:

config.EnableSwagger(c => {... c.OperationFilter<FileOperationFilter>(); ... });

To top this up, I also added a FilterAttribute to filter out Multipart content:

public class ValidateMimeMultipartContentFilter : ActionFilterAttribute
{
    public override void OnActionExecuting(HttpActionContext actionContext)
    {
        if (!actionContext.Request.Content.IsMimeMultipartContent())
        {
            throw new HttpResponseException(HttpStatusCode.UnsupportedMediaType);
        }
    }

    public override void OnActionExecuted(HttpActionExecutedContext actionExecutedContext)
    {

    }

}
Share:
13,168
martijn
Author by

martijn

Programming since 2009, C# since 2011

Updated on July 18, 2022

Comments

  • martijn
    martijn almost 2 years

    I am implementing a swagger interface into an existing web API. The current API controller exposes an async upload function which uses the Request.Content to transport an image asynchronously. The code that has been used is explained in this article.

    My api controller:

        [HttpPost]
        [Route("foo/bar/upload")]
        public async Task<HttpResponseMessage> Upload()
        {
            if (!Request.Content.IsMimeMultipartContent())
            {
                throw new HttpResponseException(HttpStatusCode.UnsupportedMediaType);
            }
            var provider = await Request.Content.ReadAsMultipartAsync(new InMemoryMultipartFormDataStreamProvider());
            NameValueCollection formData = provider.FormData;
            HttpResponseMessage response;
            //access files  
            IList<HttpContent> files = provider.Files;
            if (files.Count > 0)
            {
                HttpContent file1 = files[0];
                using (Stream input = await file1.ReadAsStreamAsync())
                {
                    object responseObj = ExternalProcessInputStream(input)
                    response = Request.CreateResponse(HttpStatusCode.OK, responseObj);
                }
            }
            else 
            {
                response = Request.CreateResponse(HttpStatusCode.BadRequest);
            }
            return response;
        }
    

    This works dandy, but when i expose this through swagger i have a parameterless function, which returns an error when used.

    My question is how can supply a proper value to test this method with?

  • Bauer
    Bauer about 6 years
    This is a really good solution, and it works great for me. However, how do I set the contentType?
  • Bauer
    Bauer about 6 years
    As an addendum to your answer, I found this, which does the same thing, except it adds an attribute that you can decorate your method with, as opposed to checking the operationID (which I found was annoying to figure out). tahirhassan.blogspot.co.il/2017/12/…
  • alex
    alex about 6 years
    For me this post was helpful too. Very similar: talkingdotnet.com/…
  • GMK
    GMK over 5 years
    Why did you use application/form-data as the mime type? I would have expected multipart/form-data.