unable to configure Web API for content type multipart

后端 未结 5 1428
情书的邮戳
情书的邮戳 2020-12-30 01:48

I am working on Web API\'s - Web API 2. My basic need is to create an API to update profile of the user. In this the ios and android will send me the request in multipart/fo

相关标签:
5条回答
  • 2020-12-30 02:14

    Not so sure this would be helpful in your case , have a look

    mvc upload file with model - second parameter posted file is null

    and

    ASP.Net MVC - Read File from HttpPostedFileBase without save

    0 讨论(0)
  • 2020-12-30 02:24

    So, what worked for me is -

    [Route("api/Account/UpdateProfile")]
    [HttpPost]
    public Task<HttpResponseMessage> UpdateProfile(/* UpdateProfileModel model */)
    {
         string root = HttpContext.Current.Server.MapPath("~/App_Data");
            var provider = new MultipartFormDataStreamProvider(root);
            await Request.Content.ReadAsMultipartAsync(provider);
            foreach (MultipartFileData file in provider.FileData)
            {
    
            }
    }
    

    Also -

    config.Formatters.JsonFormatter.SupportedMediaTypes.Add(new MediaTypeHeaderValue("multipart/form-data"));
    

    isn't required.

    I guess the multipart/form-data is internally handled somewhere after the form is submitted.

    Very clearly described here -

    http://www.asp.net/web-api/overview/advanced/sending-html-form-data-part-2

    0 讨论(0)
  • 2020-12-30 02:26

    The answer provided by JPgrassi is what you would be doing to have MultiPart data. I think there are few more things that needs to be added, so I thought of writing my own answer.

    MultiPart form data, as the name suggest, is not single type of data, but specifies that the form will be sent as a MultiPart MIME message, so you cannot have predefined formatter to read all the contents. You need to use ReadAsync function to read byte stream and get your different types of data, identify them and de-serialize them.

    There are two ways to read the contents. First one is to read and keep everything in memory and the second way is to use a provider that will stream all the file contents into some randomly name files(with GUID) and providing handle in form of local path to access file (The example provided by jpgrassi is doing the second).

    First Method: Keeping everything in-memory

    //Async because this is asynchronous process and would read stream data in a buffer. 
    //If you don't make this async, you would be only reading a few KBs (buffer size) 
    //and you wont be able to know why it is not working
    public async Task<HttpResponseMessage> Post()
    {
    
    if (!request.Content.IsMimeMultipartContent()) return null;
    
            Dictionary<string, object> extractedMediaContents = new Dictionary<string, object>();
    
            //Here I am going with assumption that I am sending data in two parts, 
            //JSON object, which will come to me as string and a file. You need to customize this in the way you want it to.           
            extractedMediaContents.Add(BASE64_FILE_CONTENTS, null);
            extractedMediaContents.Add(SERIALIZED_JSON_CONTENTS, null);
    
            request.Content.ReadAsMultipartAsync()
                    .ContinueWith(multiPart =>
                    {
                        if (multiPart.IsFaulted || multiPart.IsCanceled)
                        {
                            Request.CreateErrorResponse(HttpStatusCode.InternalServerError, multiPart.Exception);
                        }
    
                        foreach (var part in multiPart.Result.Contents)
                        {
                            using (var stream = part.ReadAsStreamAsync())
                            {
                                stream.Wait();
                                Stream requestStream = stream.Result;
    
                                using (var memoryStream = new MemoryStream())
                                {
                                    requestStream.CopyTo(memoryStream);
                                    //filename attribute is identifier for file vs other contents.
                                    if (part.Headers.ToString().IndexOf("filename") > -1)
                                    {                                        
                                        extractedMediaContents[BASE64_FILE_CONTENTS] = memoryStream.ToArray();
                                    }
                                    else
                                    {
                                        string jsonString = System.Text.Encoding.ASCII.GetString(memoryStream.ToArray());
                                       //If you need just string, this is enough, otherwise you need to de-serialize based on the content type. 
                                       //Each content is identified by name in content headers.
                                       extractedMediaContents[SERIALIZED_JSON_CONTENTS] = jsonString;
                                    }
                                }
                            }
                        }
                    }).Wait();
    
            //extractedMediaContents; This now has the contents of Request in-memory.
    }
    

    Second Method: Using a provider (as given by jpgrassi)

    Point to note, this is only filename. If you want process file or store at different location, you need to stream read the file again.

     public async Task<HttpResponseMessage> Post()
    {
    HttpResponseMessage response;
    
        //Check if request is MultiPart
        if (!Request.Content.IsMimeMultipartContent())
        {
            throw new HttpResponseException(HttpStatusCode.UnsupportedMediaType);
        }
        //This specifies local path on server where file will be created
        string root = HttpContext.Current.Server.MapPath("~/App_Data");
        var provider = new MultipartFormDataStreamProvider(root);
    
        //This write the file in your App_Data with a random name
        await Request.Content.ReadAsMultipartAsync(provider);
    
        foreach (MultipartFileData file in provider.FileData)
        {
            //Here you can get the full file path on the server
            //and other data regarding the file
            //Point to note, this is only filename. If you want to keep / process file, you need to stream read the file again.
            tempFileName = file.LocalFileName;
        }
    
        // You values are inside FormData. You can access them in this way
        foreach (var key in provider.FormData.AllKeys)
        {
            foreach (var val in provider.FormData.GetValues(key))
            {
                Trace.WriteLine(string.Format("{0}: {1}", key, val));
            }
        }
    
        //Or directly (not safe)    
        string name = provider.FormData.GetValues("name").FirstOrDefault();
    
    
        response = Request.CreateResponse(HttpStatusCode.Ok);              
    
    return response;
    }
    
    0 讨论(0)
  • 2020-12-30 02:29

    By default there is not a media type formatter built into the api that can handle multipart/form-data and perform model binding. The built in media type formatters are :

     JsonMediaTypeFormatter: application/json, text/json
     XmlMediaTypeFormatter: application/xml, text/xml
     FormUrlEncodedMediaTypeFormatter: application/x-www-form-urlencoded
     JQueryMvcFormUrlEncodedFormatter: application/x-www-form-urlencoded
    

    This is the reason why most answers involve taking over responsibility to read the data directly from the request inside the controller. However, the Web API 2 formatter collection is meant to be a starting point for developers and not meant to be the solution for all implementations. There are other solutions that have been created to create a MediaFormatter that will handle multipart form data. Once a MediaTypeFormatter class has been created it can be re-used across multiple implementations of Web API.

    How create a MultipartFormFormatter for ASP.NET 4.5 Web API

    You can download and build the full implementation of the web api 2 source code and see that the default implementations of media formatters do not natively process multi part data. https://aspnetwebstack.codeplex.com/

    0 讨论(0)
  • 2020-12-30 02:32

    You can't have parameters like that in your controller because there's no built-in media type formatter that handles Multipart/Formdata. Unless you create your own formatter, you can access the file and optional fields accessing via a MultipartFormDataStreamProvider :

    Post Method

     public async Task<HttpResponseMessage> Post()
    {
        HttpResponseMessage response;
    
            //Check if request is MultiPart
            if (!Request.Content.IsMimeMultipartContent())
            {
                throw new HttpResponseException(HttpStatusCode.UnsupportedMediaType);
            }
    
            string root = HttpContext.Current.Server.MapPath("~/App_Data");
            var provider = new MultipartFormDataStreamProvider(root);
    
            //This write the file in your App_Data with a random name
            await Request.Content.ReadAsMultipartAsync(provider);
    
            foreach (MultipartFileData file in provider.FileData)
            {
                //Here you can get the full file path on the server
                //and other data regarding the file
                tempFileName = file.LocalFileName;
            }
    
            // You values are inside FormData. You can access them in this way
            foreach (var key in provider.FormData.AllKeys)
            {
                foreach (var val in provider.FormData.GetValues(key))
                {
                    Trace.WriteLine(string.Format("{0}: {1}", key, val));
                }
            }
    
            //Or directly (not safe)    
            string name = provider.FormData.GetValues("name").FirstOrDefault();
    
    
            response = Request.CreateResponse(HttpStatusCode.Ok);              
    
        return response;
    }
    

    Here's a more detailed list of examples: Sending HTML Form Data in ASP.NET Web API: File Upload and Multipart MIME

    0 讨论(0)
提交回复
热议问题