Ответ 1
Проблема заключается в том, что с помощью Web Api тело можно читать только один раз. У меня был запущен HTTP-модуль, который регистрировал все детали запроса и читал его.
Я пытаюсь извлечь некоторые данные из запроса в новом Asp.Net Web Api. У меня есть настройка обработчика следующим образом:
public class MyTestHandler : DelegatingHandler
{
protected override System.Threading.Tasks.Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, System.Threading.CancellationToken cancellationToken)
{
if (request.Content.IsFormData())
{
request.Content.ReadAsStreamAsync().ContinueWith(x => {
var result = "";
using (var sr = new StreamReader(x.Result))
{
result = sr.ReadToEnd();
}
Console.Write(result);
});
}
return base.SendAsync(request, cancellationToken);
}
}
Это мой http-запрос:
POST http://127.0.0.1/test HTTP/1.1
Connection: Keep-Alive
Content-Length: 29
Content-Type: application/x-www-form-urlencoded
Expect: 100-continue
Host: 127.0.0.1
my_property=my_value
проблема заключается в том, что независимо от того, как я пытаюсь прочитать информацию из request.Content
, она всегда пуста. Я пробовал
request.Content.ReadAsStreamAsync
request.Content.ReadAsFormDataAsync
request.Content.ReadAs<FormDataCollection>
а также
[HttpGet,HttpPost]
public string Index([FromBody]string my_property)
{
//my_property == null
return "Test";
}
Нет, если он работает. Я не могу получить данные из тела. Я размещаю внутри IIS в Windows 7 и используя Fiddler для отправки запроса. Что я делаю неправильно?
Проблема заключается в том, что с помощью Web Api тело можно читать только один раз. У меня был запущен HTTP-модуль, который регистрировал все детали запроса и читал его.
Это некрасиво, но вы, кажется, от первоначального вмешательства, что фактически можете заменить Content в DelegatingHandler...
protected override Task SendAsync(
HttpRequestMessage request,
CancellationToken cancellationToken)
{
Stream stream = new MemoryStream();
request.Content.ReadAsStreamAsync().Result.CopyTo(stream);
stream.Seek(0,SeekOrigin.Begin);
// copy off the content "for later"
string query = new StreamReader(stream).ReadToEnd();
stream.Seek(0,SeekOrigin.Begin);
// if further processing depends on content type
// go ahead and grab current value
var contentType = request.Content.Headers.ContentType;
request.Content = new StreamContent(stream);
request.Content.Headers.ContentType = contentType;
return base.SendAsync(request, cancellationToken);
}
Я понятия не имею, является ли это хорошей формой или плохим (подозрительный плохой), но... похоже, что он работает и следует модели, которую я видел, рекомендованной для тех, кому необходимо изменить заголовки и контент запроса "по пути в" с DelegatingHandler.
Ваш пробег может существенно различаться.
Я основывал свой ответ на brmore-коде;
Эта функция может безопасно читать содержимое в любом обработчике
private string SafeReadContentFrom(HttpRequestMessage request)
{
var contentType = request.Content.Headers.ContentType;
var contentInString = request.Content.ReadAsStringAsync().Result;
request.Content = new StringContent(contentInString);
request.Content.Headers.ContentType = contentType;
return contentInString;
}
Это работает для меня.
[HttpPost]
public IHttpActionResult Index(HttpRequestMessage request)
{
var form = request.Content.ReadAsFormDataAsync().Result;
return Ok();
}
У меня была такая же проблема и, наконец, я решил не писать контент в журналах. Я живу с протоколированием Content-Type и Content-Length.
Но всегда рекомендуется писать все содержимое журналов, насколько это возможно.
Но похоже, что с помощью WebApi в настоящее время мы не можем этого достичь.
Сначала вы можете создать поставщика. MultipartMemoryStreamProvider()
то Request.Content.ReadAsMultipartAsync(provider)
;
затем прочитайте содержимое
public async Task<IHttpActionResult> Post(int id, string type)
{
// Check if the request contains multipart/form-data.
if(!Request.Content.IsMimeMultipartContent("form-data"))
return BadRequest("Unsupported media type");
try
{
var azureManager = new AzureManager();
var imageManager = new ImageManager();
var provider = new MultipartMemoryStreamProvider();
await Request.Content.ReadAsMultipartAsync(provider);
var assets = new List<Asset>();
foreach (var file in provider.Contents)
{
var stream = await file.ReadAsStreamAsync();
var guid = Guid.NewGuid();
string blobName = guid.ToString();
await azureManager.UploadAsync(blobName, stream);
var asset = new Asset
{
PropertyId = id,
FileId = guid,
FileName = file.Headers.ContentDisposition.FileName.Trim('\"').ToLower(),
FileSize = file.Headers.ContentLength ?? 0,
MimeType = file.Headers.ContentType.MediaType.ToLower()
};
if (type == "photos")
{
asset.Type = AssetType.Photo;
// Resize and crop copies to 16:9
using (MemoryStream thumb = imageManager.ResizeImage(stream, 320, 180))
{
await azureManager.UploadAsync(blobName, thumb, BlobContainers.Thumbs);
}
using (MemoryStream photo = imageManager.ResizeImage(stream, 1024, 576))
{
await azureManager.UploadAsync(blobName, photo, BlobContainers.Photos);
}
}
else
asset.AssumeType();
assets.Add(asset);
}
db.Assets.AddRange(assets);
await db.SaveChangesAsync();
return Ok(new { Message = "Assets uploaded ok", Assets = assets });
}
catch (Exception ex)
{
return BadRequest(ex.GetBaseException().Message);
}
}