WebAPI не может анализировать сообщение multipart/form-data
Я пытаюсь принять сообщение от клиента (приложение iOS), и мой код продолжает терпеть неудачу при чтении потока. Говорит, что сообщение не завершено. Я пытался заставить эту работу часами, кажется, что-то не так в моем формате сообщения или что-то в этом роде. Все, что я пытаюсь сделать, это прочитать строку, но разработчик, с которым я работаю, который выполняет роль iOS, знает, как отправлять multipart/form-data, а не контент-тип json.
Вот точная ошибка:
Неожиданный конец многопоточного потока MIME. Сообщение MIME multipart не является полным. "
Здесь не удается: await Request.Content.ReadAsMultipartAsync(provider);
Headers:
POST http://localhost:8603/api/login HTTP/1.1
Host: localhost:8603
Accept-Encoding: gzip,deflate
Content-Type: multipart/form-data; boundary=------------nx-oauth216807
Content-Length: 364
Accept-Language: en-us
Accept: */*
Connection: keep-alive
Тело
--------------nx-oauth216807
Content-Disposition: form-data; name="token"
CAAH5su8bZC1IBAC3Qk4aztKzisZCd2Muc3no4BqVUycnZAFSKuleRU7V9uZCbc8DZCedYQTIFKwJbVZCANJCs4ZCZA654PgA22Nei9KiIMLsGbZBaNQugouuLNafNqIOTs9wDvD61ZA6WSTd73AVtFp9tQ1PmFGz601apUGHSimYZCjLfGBo40EBQ5z6eSMNiFeSylym1pK4PCvI17fXCmOcRix4cs96EBl8ZA1opGKVuWizOsS0WZCMiVGvT
--------------nx-oauth216807--
Вот код WebAPI:
public async Task<HttpResponseMessage> PostFormData()
{
// Check if the request contains multipart/form-data.
if (!Request.Content.IsMimeMultipartContent())
{
throw new HttpResponseException(HttpStatusCode.UnsupportedMediaType);
}
try
{
string root = HttpContext.Current.Server.MapPath("~/App_Data");
var provider = new MultipartFormDataStreamProvider(root);
// Read the form data and return an async task.
await Request.Content.ReadAsMultipartAsync(provider);
// This illustrates how to get the file names.
foreach (MultipartFileData file in provider.FileData)
{
Trace.WriteLine(file.Headers.ContentDisposition.FileName);
Trace.WriteLine("Server file path: " + file.LocalFileName);
}
return Request.CreateResponse(HttpStatusCode.OK);
}
catch (System.Exception e)
{
return Request.CreateErrorResponse(HttpStatusCode.InternalServerError, e);
}
}
Ответы
Ответ 1
В моем приложении периодически возникала и эта ошибка. Обновление до WEB API 2.1 ничего не делало, и сообщение об исключении совершенно бесполезно.
Я думаю, что на самом деле это происходит, но это задыхается от больших файлов. Увеличение моих максимальных пределов запроса в web.config, похоже, исправило это.
<system.web>
<httpRuntime maxRequestLength="30000" />
</system.web>
<system.webServer>
<security>
<requestFiltering>
<requestLimits maxAllowedContentLength="30000" />
</requestFiltering>
</security>
</system.webServer>
(Это устанавливает потолок в 30 мегабайт. Установите его на все, что вам нужно. Подробнее здесь)
Ответ 2
Я тоже столкнулся с этой ошибкой. InnerException
Cannot access a disposed object.
Это означает, что что-то читает ваш поток перед вашим вызовом ReadAsMultipartAsync
.
Где-то перед этим вызовом Request.Content.ReadAsMultipartAsync(provider)
вы можете позвонить
Request.Content.LoadIntoBufferAsync().Wait()
, который загрузит этот поток в буфер и позволит вам читать его более одного раза.
Это не оптимальное решение, но оно работает.
Ответ 3
Я оставляю это здесь, так как мне потребовалось некоторое время, пробовав другие обходные пути, пока я не столкнулся с следующим полезным ответом, и некоторые люди, имеющие эту проблему, могут оказаться в этом сообщении.
A\r\n необходимо добавить в конец потока содержимого запроса.
Вместо использования этой строки для чтения данных:
await Request.Content.ReadAsMultipartAsync(provider);
Вам нужно будет:
-
Загрузка потока запросов в память
-
добавить строку \r\n, которая требуется
-
создать содержимое потока из содержимого памяти
-
вручную добавить заголовки запросов в контент потока
-
Наконец, используйте это вместо:
streamContent.ReadAsMultipartAsync(provider);
Проверьте ответ Landuber Kassa на полный код: ASP.NET Web API, неожиданный конец многочастного потока MIME при загрузке из Flex FileReference
Ответ 4
Просто модификация для ответа Шоу Левина на тот случай, если кто-то захочет его использовать.
boundary = value.Substring(0, value.IndexOf("\r\n"));
найдет первое появление CRLF, вы должны изменить его на boundary = value.Substring(0, value.LastIndexOf("\r\n"));
, чтобы он выглядел последним. В противном случае, если контент содержит CRLF где-то посередине, вы потеряете часть данных в запросе.
Ответ 5
Были похожие сообщения об ошибках, для некоторых из них работало следующее: упомянуть атрибут Id = ", name=" "для управления загрузкой файла html, благодаря загрузке WebAPI ошибка. Ожидаемый конец многоточечного потока MIME. Сообщение MIME не завершено
Но в моем случае это не разрешилось с помощью простого простого тэка:(
Ответ 6
Я бы не рекомендовал этот ответ - надеюсь, теперь есть лучший способ.
Кто-то спросил, так вот мой пользовательский парсер, который отлично работает:
Граница исходит отсюда:
string value;
using (var reader = new StreamReader(tempStream, Encoding.UTF8))
{
value = reader.ReadToEnd();
// Do something with the value
}
boundary = value.Substring(0, value.IndexOf("\r\n"));
И затем мы проанализируем содержимое запроса здесь:
public Dictionary<string, BodyContent> ParseContent(string content)
{
string[] list = content.Split(new string[] { boundary }, StringSplitOptions.RemoveEmptyEntries);
string name="", val="";
Dictionary<string, BodyContent> temp = new Dictionary<string, BodyContent>();
foreach (String s in list)
{
if (s == "--" || s == "--\r\n")
{
//Do nothing.
}
else
{
string[] token = s.Split(new string[] { "\r\n" }, StringSplitOptions.RemoveEmptyEntries);
val = "";
name = "";
foreach (string x in token)
{
if(x.StartsWith("Content-Disposition"))
{
//Name
name = x.Substring(x.IndexOf("name=")+5, x.Length - x.IndexOf("name=")-5);
name = name.Replace(@"\","");
name = name.Replace("\"","");
}
if (x.StartsWith("--"))
{
break;
}
if (!x.StartsWith("--") && !x.StartsWith("Content-Disposition"))
{
val = x;
}
}
if (name.Length > 0)
{
BodyContent b = new BodyContent();
b.content = name;
if (val.Length == 0)
{
b.value = "";
}
else
{
b.value = val;
}
temp.Add(name, b);
}
}
}
return temp;
}