Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

API File Upload using HTTP content not exposed in swagger

Tags:

c#

api

swagger

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?

like image 433
martijn Avatar asked Feb 24 '17 09:02

martijn


People also ask

How do you pass the multipart form data in swagger?

You start with the requestBody/content keyword. Then, you specify the media type of request data. File uploads typically use the multipart/form-data media type, and mixed-data requests usually use multipart/mixed . Below the media type, put the schema keyword to indicate that you start describing the request payload.

How does HTTP multipart work?

​ A multipart request is a HTTP request that HTTP clients construct to send files and data over to a HTTP Server. It is commonly used by browsers and HTTP clients to upload files to the server.


1 Answers

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)
    {

    }

}
like image 165
Bjorn Bailleul Avatar answered Sep 29 '22 06:09

Bjorn Bailleul