Не удалось настроить веб-API для типа контента multipart

Я работаю над Web API - веб-API 2. Моя основная потребность - создать API для обновления профиля пользователя. В этом ios и android отправят мне запрос в multipart/form-data. Они отправят мне несколько параметров с изображением. Но всякий раз, когда я пытаюсь создать API, моя модель становится нулевой каждый раз.

Я также добавил эту строку в WebApiConfig:

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

Это мой класс:

public class UpdateProfileModel
{
   public HttpPostedFileBase ProfileImage { get; set; }
   public string Name { get; set; }
}

Это мой контроллер:

[Route("api/Account/UpdateProfile")]
[HttpPost]
public HttpResponseMessage UpdateProfile(UpdateProfileModel model)
{
}

Я даже не получаю значения параметров в моей модели. Я что-то делаю неправильно?

Ни один из ответов, связанных с этим, не был полезен для меня. Его около 3-го дня, и я пробовал почти все и каждый метод. но я не могу этого достичь.

Хотя я могу использовать это, но это, как показано ниже, но это не похоже на хороший подход. поэтому я избегаю этого.

var httpRequest = HttpContext.Current.Request;
if (httpRequest.Form["ParameterName"] != null)
{
    var parameterName = httpRequest.Form["ParameterName"];
}

и для файлов я могу это сделать:

if (httpRequest.Files.Count > 0)
{
     //i can access my files here and save them
}

Пожалуйста, помогите, если у вас есть хороший подход к нему. Или объясните мне, почему я не могу получить эти значения в модели.

Большое спасибо в Advance

Ответы

Ответ 1

По умолчанию в api нет форматирования типа медиафайла, который может обрабатывать данные multipart/form и выполнять привязку модели. Формируемыми форматами медиаформатов являются:

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

Вот почему большинство ответов связано с взятием на себя ответственности за чтение данных непосредственно из запроса внутри контроллера. Однако сборка форматирования Web API 2 предназначена для разработчиков и не предназначена для всех реализаций. Существуют и другие решения, которые были созданы для создания MediaFormatter, который будет обрабатывать данные многостраничной формы. После создания класса MediaTypeFormatter его можно повторно использовать для нескольких реализаций веб-API.

Как создать MultipartFormFormatter для веб-API ASP.NET 4.5

Вы можете загружать и строить полную реализацию исходного веб-кода api 2 и видеть, что стандартные реализации медиаформатов по умолчанию не обрабатывают данные из нескольких частей. https://aspnetwebstack.codeplex.com/

Ответ 2

Ответ, предоставленный JPgrassi, - это то, что вы делаете, чтобы иметь данные MultiPart. Я думаю, что есть еще несколько вещей, которые нужно добавить, поэтому я подумал о написании собственного ответа.

Данные формы MultiPart, как следует из названия, не являются отдельными типами данных, но указывают, что форма будет отправлена ​​как сообщение MultiPart MIME, поэтому у вас не может быть предопределенного форматирования для чтения всего содержимого. Вам необходимо использовать функцию ReadAsync для чтения байтового потока и получения различных типов данных, определения их и де-сериализации.

Существует два способа чтения содержимого. Первый заключается в том, чтобы читать и хранить все в памяти, а второй способ - использовать поставщика, который будет передавать все содержимое файла в некоторые файлы с произвольным именем (с GUID) и предоставлять дескриптор в виде локального пути для доступа к файлу (пример предоставлен jpgrassi делает второе).

Первый метод: сохранение всего в памяти

//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.
}

Второй метод: использование провайдера (как указано jpgrassi)

Обратите внимание, что это только имя файла. Если вы хотите, чтобы файл процесса или хранилище находился в другом месте, вам нужно снова передать файл.

 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;
}

Ответ 3

У вас не может быть таких параметров в вашем контроллере, потому что нет встроенного форматера медиафайлов, который обрабатывает Multipart/Formdata. Если вы не создаете собственный форматтер, вы можете получить доступ к файлу и необязательным полям, доступным через MultipartFormDataStreamProvider:

Метод отправки

 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;
}

Здесь приведен более подробный список примеров: Отправка данных формы HTML в веб-API ASP.NET: загрузка файлов и многостраничный MIME

Ответ 5

Итак, что сработало для меня -

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

        }
}

Также -

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

не требуется.

Я предполагаю, что данные о multipart/form обрабатываются внутри где-то после отправки формы.

Очень четко описано здесь -

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