// Copyright (c) Microsoft Corporation. All rights reserved. See License.txt in the project root for license information.
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Diagnostics.CodeAnalysis;
using System.Diagnostics.Contracts;
using System.Linq;
using System.Linq.Expressions;
using System.Net;
using System.Net.Http;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Threading.Tasks;
using System.Web.Http.Filters;
using System.Web.Http.Internal;
using System.Web.Http.Properties;
namespace System.Web.Http.Controllers
{
///
/// An action descriptor representing a reflected synchronous or asynchronous action method.
///
public class ReflectedHttpActionDescriptor : HttpActionDescriptor
{
private static readonly object[] _empty = new object[0];
private readonly Lazy> _parameters;
private Lazy _actionExecutor;
private MethodInfo _methodInfo;
private Type _returnType;
private string _actionName;
private Collection _supportedHttpMethods;
// Getting custom attributes via reflection is slow.
// But iterating over a object[] to pick out specific types is fast.
// Furthermore, many different services may call to ask for different attributes, so we have multiple callers.
// That means there's not a single cache for the callers, which means there's some value caching here.
// This cache can be a 2x speedup in some benchmarks.
private object[] _attrCached;
private static readonly HttpMethod[] _supportedHttpMethodsByConvention =
{
HttpMethod.Get,
HttpMethod.Post,
HttpMethod.Put,
HttpMethod.Delete,
HttpMethod.Head,
HttpMethod.Options,
new HttpMethod("PATCH")
};
///
/// Initializes a new instance of the class.
///
/// The default constructor is intended for use by unit testing only.
public ReflectedHttpActionDescriptor()
{
_parameters = new Lazy>(() => InitializeParameterDescriptors());
_supportedHttpMethods = new Collection();
}
public ReflectedHttpActionDescriptor(HttpControllerDescriptor controllerDescriptor, MethodInfo methodInfo)
: base(controllerDescriptor)
{
if (methodInfo == null)
{
throw Error.ArgumentNull("methodInfo");
}
InitializeProperties(methodInfo);
_parameters = new Lazy>(() => InitializeParameterDescriptors());
}
///
/// Caches that the ActionSelector use.
///
internal IActionMethodSelector[] CacheAttrsIActionMethodSelector { get; private set; }
public override string ActionName
{
get { return _actionName; }
}
public override Collection SupportedHttpMethods
{
get { return _supportedHttpMethods; }
}
public MethodInfo MethodInfo
{
get { return _methodInfo; }
set
{
if (value == null)
{
throw Error.PropertyNull();
}
InitializeProperties(value);
}
}
///
/// The return type of the method or null if the method does not return a value (e.g. a method returning
/// void).
///
///
/// This implementation returns the exact value of for
/// synchronous methods and an unwrapped value for asynchronous methods (e.g. the T of .
/// This returns null for methods returning void or .
///
public override Type ReturnType
{
get { return _returnType; }
}
public override Collection GetCustomAttributes()
{
Contract.Assert(_methodInfo != null); // can't get attributes without the method set!
Contract.Assert(_attrCached != null); // setting the method should build the attribute cache
return new Collection(TypeHelper.OfType(_attrCached));
}
///
/// Executes the described action and returns a that once completed will
/// contain the return value of the action.
///
/// The context.
/// The arguments.
/// A that once completed will contain the return value of the action.
public override Task