using System; using System.Collections.Generic; using System.Linq; using System.Reflection; using System.Threading.Tasks; using Microsoft.AspNet.Abstractions; using Microsoft.AspNet.Mvc.Internal; using Microsoft.AspNet.DependencyInjection; using Microsoft.AspNet.Mvc.ModelBinding; using Microsoft.AspNet.DependencyInjection.NestedProviders; using Microsoft.AspNet.Mvc.Filters; namespace Microsoft.AspNet.Mvc { public class ReflectedActionInvoker : IActionInvoker { private readonly ActionContext _actionContext; private readonly ReflectedActionDescriptor _descriptor; private readonly IActionResultFactory _actionResultFactory; private readonly IServiceProvider _serviceProvider; private readonly IControllerFactory _controllerFactory; private readonly IActionBindingContextProvider _bindingProvider; private readonly INestedProviderManager _filterProvider; public ReflectedActionInvoker(ActionContext actionContext, ReflectedActionDescriptor descriptor, IActionResultFactory actionResultFactory, IControllerFactory controllerFactory, IActionBindingContextProvider bindingContextProvider, INestedProviderManager filterProvider, IServiceProvider serviceProvider) { _actionContext = actionContext; _descriptor = descriptor; _actionResultFactory = actionResultFactory; _controllerFactory = controllerFactory; _bindingProvider = bindingContextProvider; _filterProvider = filterProvider; _serviceProvider = serviceProvider; } public async Task InvokeActionAsync() { IActionResult actionResult; var context = new FilterProviderContext(_descriptor); _filterProvider.Invoke(context); var modelState = new ModelStateDictionary(); object controller = _controllerFactory.CreateController(_actionContext, modelState); if (controller == null) { actionResult = new HttpStatusCodeResult(404); } else { var method = _descriptor.MethodInfo; if (method == null) { actionResult = new HttpStatusCodeResult(404); } else { var parameterValues = await GetParameterValues(modelState); var authZFilters = context.AuthorizationFilters; if (authZFilters != null && authZFilters.Count > 0) { var authZEndPoint = new AuthorizationFilterEndPoint(); authZFilters.Add(authZEndPoint); var authZContext = new AuthorizationFilterContext(_actionContext); var authZPipeline = new FilterPipelineBuilder(authZFilters, authZContext); await authZPipeline.InvokeAsync(); if (authZContext.ActionResult == null && !authZContext.HasFailed && authZEndPoint.EndPointCalled) { actionResult = null; } else { // User cleaned out the result but we failed or short circuited the end point. actionResult = authZContext.ActionResult ?? new HttpStatusCodeResult(401); } } else { actionResult = null; } if (actionResult == null) { var actionFilters = context.ActionFilters ?? new List(); var actionFilterContext = new ActionFilterContext(_actionContext, parameterValues, method.ReturnType); // TODO: This is extremely temporary and is going to get soon replaced with the action executer var actionEndPoint = new ReflectedActionFilterEndPoint(async (inArray) => method.Invoke(controller, inArray), _actionResultFactory); actionFilters.Add(actionEndPoint); var actionFilterPipeline = new FilterPipelineBuilder(actionFilters, actionFilterContext); await actionFilterPipeline.InvokeAsync(); actionResult = actionFilterContext.Result; } } } var actionResultFilters = context.ActionResultFilters ?? new List(); var actionResultFilterContext = new ActionResultFilterContext(_actionContext, actionResult); var actionResultFilterEndPoint = new ActionResultFilterEndPoint(); actionResultFilters.Add(actionResultFilterEndPoint); var actionResultPipeline = new FilterPipelineBuilder(actionResultFilters, actionResultFilterContext); await actionResultPipeline.InvokeAsync(); } private async Task> GetParameterValues(ModelStateDictionary modelState) { var actionBindingContext = await _bindingProvider.GetActionBindingContextAsync(_actionContext); var parameters = _descriptor.Parameters; var parameterValues = new Dictionary(parameters.Count, StringComparer.Ordinal); for (int i = 0; i < parameters.Count; i++) { var parameter = parameters[i]; if (parameter.BodyParameterInfo != null) { var inputFormatterContext = actionBindingContext.CreateInputFormatterContext( modelState, parameter); await actionBindingContext.InputFormatter.ReadAsync(inputFormatterContext); parameterValues[parameter.Name] = inputFormatterContext.Model; } else { var modelBindingContext = actionBindingContext.CreateModelBindingContext( modelState, parameter); actionBindingContext.ModelBinder.BindModel(modelBindingContext); parameterValues[parameter.Name] = modelBindingContext.Model; } } return parameterValues; } private object[] GetArgumentValues(IDictionary parameterValues) { var parameters = _descriptor.MethodInfo.GetParameters(); var arguments = new object[parameters.Length]; for (int i = 0; i < arguments.Length; i++) { object value; if (parameterValues.TryGetValue(parameters[i].Name, out value)) { arguments[i] = value; } else { arguments[i] = parameters[i].DefaultValue; } } return arguments; } } }