// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System.Collections.Generic;
using System.ComponentModel;
using System.IO;
using System.Net.Http.Formatting;
using System.Net.Http.Headers;
using System.Threading;
using System.Threading.Tasks;
using System.Web.Http;
namespace System.Net.Http
{
///
/// Extension methods to allow strongly typed objects to be read from instances.
///
[EditorBrowsable(EditorBrowsableState.Never)]
public static class HttpContentExtensions
{
private static MediaTypeFormatterCollection _defaultMediaTypeFormatterCollection = null;
// Using the JsonMediaTypeFormatter for the first time is rather expensive (due to reflection cost
// when creating the default contract resolver). Hence we new up a static collection, such
// that the second call is much faster.
private static MediaTypeFormatterCollection DefaultMediaTypeFormatterCollection
{
get
{
if (_defaultMediaTypeFormatterCollection == null)
{
_defaultMediaTypeFormatterCollection = new MediaTypeFormatterCollection();
}
return _defaultMediaTypeFormatterCollection;
}
}
///
/// Returns a that will yield an object of the specified
/// from the instance.
///
/// This override use the built-in collection of formatters.
/// The instance from which to read.
/// The type of the object to read.
/// A task object representing reading the content as an object of the specified type.
public static Task ReadAsAsync(this HttpContent content, Type type)
{
return content.ReadAsAsync(type, DefaultMediaTypeFormatterCollection);
}
///
/// Returns a that will yield an object of the specified
/// from the instance.
///
/// This override use the built-in collection of formatters.
/// The instance from which to read.
/// The type of the object to read.
/// The token to monitor for cancellation requests.
/// A task object representing reading the content as an object of the specified type.
public static Task ReadAsAsync(this HttpContent content, Type type, CancellationToken cancellationToken)
{
return content.ReadAsAsync(type, DefaultMediaTypeFormatterCollection, cancellationToken);
}
///
/// Returns a that will yield an object of the specified
/// from the instance using one of the provided
/// to deserialize the content.
///
/// The instance from which to read.
/// The type of the object to read.
/// The collection of instances to use.
/// A task object representing reading the content as an object of the specified type.
public static Task ReadAsAsync(this HttpContent content, Type type, IEnumerable formatters)
{
return ReadAsAsync(content, type, formatters, null);
}
///
/// Returns a that will yield an object of the specified
/// from the instance using one of the provided
/// to deserialize the content.
///
/// The instance from which to read.
/// The type of the object to read.
/// The collection of instances to use.
/// The token to monitor for cancellation requests.
/// A task object representing reading the content as an object of the specified type.
public static Task ReadAsAsync(this HttpContent content, Type type, IEnumerable formatters,
CancellationToken cancellationToken)
{
return ReadAsAsync(content, type, formatters, null, cancellationToken);
}
///
/// Returns a that will yield an object of the specified
/// from the instance using one of the provided
/// to deserialize the content.
///
/// The instance from which to read.
/// The type of the object to read.
/// The collection of instances to use.
/// The to log events to.
/// A task object representing reading the content as an object of the specified type.
public static Task ReadAsAsync(this HttpContent content, Type type, IEnumerable formatters,
IFormatterLogger formatterLogger)
{
return ReadAsAsync(content, type, formatters, formatterLogger);
}
///
/// Returns a that will yield an object of the specified
/// from the instance using one of the provided
/// to deserialize the content.
///
/// The instance from which to read.
/// The type of the object to read.
/// The collection of instances to use.
/// The to log events to.
/// The token to monitor for cancellation requests.
/// A task object representing reading the content as an object of the specified type.
public static Task ReadAsAsync(this HttpContent content, Type type, IEnumerable formatters,
IFormatterLogger formatterLogger, CancellationToken cancellationToken)
{
return ReadAsAsync(content, type, formatters, formatterLogger, cancellationToken);
}
///
/// Returns a that will yield an object of the specified
/// type from the instance.
///
/// This override use the built-in collection of formatters.
/// The type of the object to read.
/// The instance from which to read.
/// A task object representing reading the content as an object of the specified type.
public static Task ReadAsAsync(this HttpContent content)
{
return content.ReadAsAsync(DefaultMediaTypeFormatterCollection);
}
///
/// Returns a that will yield an object of the specified
/// type from the instance.
///
/// This override use the built-in collection of formatters.
/// The type of the object to read.
/// The instance from which to read.
/// The token to monitor for cancellation requests.
/// A task object representing reading the content as an object of the specified type.
public static Task ReadAsAsync(this HttpContent content, CancellationToken cancellationToken)
{
return content.ReadAsAsync(DefaultMediaTypeFormatterCollection, cancellationToken);
}
///
/// Returns a that will yield an object of the specified
/// type from the instance.
///
/// The type of the object to read.
/// The instance from which to read.
/// The collection of instances to use.
/// A task object representing reading the content as an object of the specified type.
public static Task ReadAsAsync(this HttpContent content, IEnumerable formatters)
{
return ReadAsAsync(content, typeof(T), formatters, null);
}
///
/// Returns a that will yield an object of the specified
/// type from the instance.
///
/// The type of the object to read.
/// The instance from which to read.
/// The collection of instances to use.
/// The token to monitor for cancellation requests.
/// A task object representing reading the content as an object of the specified type.
public static Task ReadAsAsync(this HttpContent content, IEnumerable formatters,
CancellationToken cancellationToken)
{
return ReadAsAsync(content, typeof(T), formatters, null, cancellationToken);
}
///
/// Returns a that will yield an object of the specified
/// type from the instance.
///
/// The type of the object to read.
/// The instance from which to read.
/// The collection of instances to use.
/// The to log events to.
/// A task object representing reading the content as an object of the specified type.
public static Task ReadAsAsync(this HttpContent content, IEnumerable formatters,
IFormatterLogger formatterLogger)
{
return ReadAsAsync(content, typeof(T), formatters, formatterLogger);
}
///
/// Returns a that will yield an object of the specified
/// type from the instance.
///
/// The type of the object to read.
/// The instance from which to read.
/// The collection of instances to use.
/// The to log events to.
/// The token to monitor for cancellation requests.
/// A task object representing reading the content as an object of the specified type.
public static Task ReadAsAsync(this HttpContent content, IEnumerable formatters,
IFormatterLogger formatterLogger, CancellationToken cancellationToken)
{
return ReadAsAsync(content, typeof(T), formatters, formatterLogger, cancellationToken);
}
private static Task ReadAsAsync(HttpContent content, Type type, IEnumerable formatters,
IFormatterLogger formatterLogger)
{
return ReadAsAsync(content, type, formatters, formatterLogger, CancellationToken.None);
}
// There are many helper overloads for ReadAs*(). Provide one worker function to ensure the logic is shared.
//
// For loosely typed, T = Object, type = specific class.
// For strongly typed, T == type.GetType()
private static Task ReadAsAsync(HttpContent content, Type type, IEnumerable formatters,
IFormatterLogger formatterLogger, CancellationToken cancellationToken)
{
if (content == null)
{
throw Error.ArgumentNull("content");
}
if (type == null)
{
throw Error.ArgumentNull("type");
}
if (formatters == null)
{
throw Error.ArgumentNull("formatters");
}
ObjectContent objectContent = content as ObjectContent;
if (objectContent != null && objectContent.Value != null && type.IsAssignableFrom(objectContent.Value.GetType()))
{
return Task.FromResult((T)objectContent.Value);
}
MediaTypeFormatter formatter = null;
// Default to "application/octet-stream" if there is no content-type in accordance with section 7.2.1 of the HTTP spec
MediaTypeHeaderValue mediaType = content.Headers.ContentType ?? MediaTypeConstants.ApplicationOctetStreamMediaType;
formatter = new MediaTypeFormatterCollection(formatters).FindReader(type, mediaType);
if (formatter == null)
{
if (content.Headers.ContentLength == 0)
{
T defaultValue = (T)MediaTypeFormatter.GetDefaultValueForType(type);
return Task.FromResult(defaultValue);
}
throw new UnsupportedMediaTypeException(
Error.Format(Properties.Resources.NoReadSerializerAvailable, type.Name, mediaType.MediaType),
mediaType);
}
return ReadAsAsyncCore(content, type, formatterLogger, formatter, cancellationToken);
}
private static async Task ReadAsAsyncCore(HttpContent content, Type type, IFormatterLogger formatterLogger,
MediaTypeFormatter formatter, CancellationToken cancellationToken)
{
cancellationToken.ThrowIfCancellationRequested();
Stream stream = await content.ReadAsStreamAsync();
object result = await formatter.ReadFromStreamAsync(type, stream, content, formatterLogger, cancellationToken);
return (T)result;
}
}
}