Моя цель - аутентифицировать запросы веб-API с помощью AuthorizationFilter или DelegatingHandler. Я хочу искать идентификатор клиента и токен аутентификации в нескольких местах, включая тело запроса. Сначала казалось, что это будет легко, я мог бы сделать что-то вроде этого
var task = _message.Content.ReadAsAsync<Credentials>();
task.Wait();
if (task.Result != null)
{
// check if credentials are valid
}
Проблема в том, что HttpContent можно прочитать только один раз. Если я сделаю это в обработчике или в фильтре, то контент не будет доступен для меня в моем методе действий. Я нашел несколько ответов здесь, в StackOverflow, вроде этого: Прочитайте HttpContent в контроллере WebApi, которые объясняют, что это намеренно таким образом, но они не говорят ПОЧЕМУ. Это кажется довольно серьезным ограничением, которое мешает мне использовать любой классный код анализа содержимого веб-API в фильтрах или обработчиках.
Это техническое ограничение? Это пытается удержать меня от ОЧЕНЬ ПЛОХОЙ ВЕЩИ (tm), которую я не вижу?
POSTMORTEM:
Я взглянул на источник, как предположил Филип. ReadAsStreamAsync возвращает внутренний поток и ничего не мешает вам вызвать Seek , если поток поддерживает его. В моих тестах, если я вызвал ReadAsAsync, сделал следующее:
message.Content.ReadAsStreamAsync().ContinueWith(t => t.Result.Seek(0, SeekOrigin.Begin)).Wait();
Процесс привязки автоматической модели будет работать отлично, когда он ударит мой метод действия. Я не использовал это, хотя, я выбрал что-то более прямое:
var buffer = new MemoryStream(_message.Content.ReadAsByteArrayAsync().WaitFor());
var formatters = _message.GetConfiguration().Formatters;
var reader = formatters.FindReader(typeof(Credentials), _message.Content.Headers.ContentType);
var credentials = reader.ReadFromStreamAsync(typeof(Credentials), buffer, _message.Content, null).WaitFor() as Credentials;
С помощью метода расширения (я в .NET 4.0 без ключевого слова ожидания)
public static class TaskExtensions
{
public static T WaitFor<T>(this Task<T> task)
{
task.Wait();
if (task.IsCanceled) { throw new ApplicationException(); }
if (task.IsFaulted) { throw task.Exception; }
return task.Result;
}
}
Один последний улов, HttpContent имеет размер жесткого кодированного максимального буфера:
internal const int DefaultMaxBufferSize = 65536;
Итак, если ваш контент будет больше, вам придется вручную вызвать LoadIntoBufferAsync с большим размером, прежде чем пытаться вызвать ReadAsByteArrayAsync.