From 772f833dc999cd2f7a273c9306e905c4f383a34a Mon Sep 17 00:00:00 2001 From: Yishai Galatzer Date: Wed, 5 Mar 2014 20:42:21 -0800 Subject: [PATCH] Action filters & Authorization Filters working --- .../Filters/AgeEnhancerFilterAttribute.cs | 35 ++++++++++++ .../MvcSample/Filters/PassThroughAttribute.cs | 14 +++++ samples/MvcSample/FiltersController.cs | 26 ++++----- samples/MvcSample/Models/User.cs | 1 + samples/MvcSample/Startup.cs | 2 + .../Views/{Home => Shared}/MyView.cshtml | 2 +- .../Filters/ActionFilterContext.cs | 7 ++- .../ReflectedActionDescriptorProvider.cs | 3 -- .../ReflectedActionInvoker.cs | 53 ++++++++++++------- src/Microsoft.AspNet.Mvc/MvcServices.cs | 2 +- 10 files changed, 102 insertions(+), 43 deletions(-) create mode 100644 samples/MvcSample/Filters/AgeEnhancerFilterAttribute.cs create mode 100644 samples/MvcSample/Filters/PassThroughAttribute.cs rename samples/MvcSample/Views/{Home => Shared}/MyView.cshtml (97%) diff --git a/samples/MvcSample/Filters/AgeEnhancerFilterAttribute.cs b/samples/MvcSample/Filters/AgeEnhancerFilterAttribute.cs new file mode 100644 index 0000000000..7b21cb8e0e --- /dev/null +++ b/samples/MvcSample/Filters/AgeEnhancerFilterAttribute.cs @@ -0,0 +1,35 @@ +using System; +using System.Threading.Tasks; +using Microsoft.AspNet.Mvc; + +namespace MvcSample.Filters +{ + public class AgeEnhancerAttribute : ActionFilterAttribute + { + public async override Task Invoke(ActionFilterContext context, Func next) + { + object age = null; + + if (context.ActionParameters.TryGetValue("age", out age)) + { + if (age is int) + { + var intAge = (int) age; + + if (intAge < 21) + { + intAge += 5; + } + else if (intAge > 30) + { + intAge = 29; + } + + context.ActionParameters["age"] = intAge; + } + } + + await next(); + } + } +} diff --git a/samples/MvcSample/Filters/PassThroughAttribute.cs b/samples/MvcSample/Filters/PassThroughAttribute.cs new file mode 100644 index 0000000000..c6d8237785 --- /dev/null +++ b/samples/MvcSample/Filters/PassThroughAttribute.cs @@ -0,0 +1,14 @@ +using System; +using System.Threading.Tasks; +using Microsoft.AspNet.Mvc; + +namespace MvcSample +{ + public class PassThroughAttribute : AuthorizationFilterAttribute + { + public async override Task Invoke(AuthorizationFilterContext context, Func next) + { + await next(); + } + } +} diff --git a/samples/MvcSample/FiltersController.cs b/samples/MvcSample/FiltersController.cs index 8dd85ec9d2..528cb49109 100644 --- a/samples/MvcSample/FiltersController.cs +++ b/samples/MvcSample/FiltersController.cs @@ -1,14 +1,13 @@ -using System; -using System.Threading.Tasks; -using Microsoft.AspNet.Mvc; +using Microsoft.AspNet.Mvc; +using MvcSample.Filters; using MvcSample.Models; namespace MvcSample { // Expected order in descriptor - object -> int -> string // TODO: Add a real filter here - [ServiceFilter(typeof(object), Order = 1)] - [ServiceFilter(typeof(string))] + [ServiceFilter(typeof(PassThroughAttribute), Order = 1)] + [ServiceFilter(typeof(PassThroughAttribute))] [PassThrough(Order = 0)] [PassThrough(Order = 2)] public class FiltersController : Controller @@ -16,18 +15,13 @@ namespace MvcSample private readonly User _user = new User() { Name = "User Name", Address = "Home Address" }; // TODO: Add a real filter here - [ServiceFilter(typeof(int))] - public IActionResult Index() + [ServiceFilter(typeof(PassThroughAttribute))] + [AgeEnhancer] + public IActionResult Index(int age) { + _user.Age = age; + return View("MyView", _user); } - } - - public class PassThroughAttribute : AuthorizationFilterAttribute - { - public async override Task Invoke(AuthorizationFilterContext context, Func next) - { - await next(); - } - } + } } \ No newline at end of file diff --git a/samples/MvcSample/Models/User.cs b/samples/MvcSample/Models/User.cs index 877d54e0b0..4ce4ebc24f 100644 --- a/samples/MvcSample/Models/User.cs +++ b/samples/MvcSample/Models/User.cs @@ -4,5 +4,6 @@ { public string Name { get; set; } public string Address { get; set; } + public int Age { get; set; } } } \ No newline at end of file diff --git a/samples/MvcSample/Startup.cs b/samples/MvcSample/Startup.cs index b0faac7132..92d2b37fc2 100644 --- a/samples/MvcSample/Startup.cs +++ b/samples/MvcSample/Startup.cs @@ -36,6 +36,8 @@ namespace MvcSample var services = MvcServices.GetDefaultServices(configuration, _env); var serviceProvider = new ServiceProvider().Add(services); + serviceProvider.AddInstance(new PassThroughAttribute()); + var routes = new RouteCollection() { DefaultHandler = new MvcApplication(serviceProvider), diff --git a/samples/MvcSample/Views/Home/MyView.cshtml b/samples/MvcSample/Views/Shared/MyView.cshtml similarity index 97% rename from samples/MvcSample/Views/Home/MyView.cshtml rename to samples/MvcSample/Views/Shared/MyView.cshtml index 6f05da0cf1..1b4d654c4c 100644 --- a/samples/MvcSample/Views/Home/MyView.cshtml +++ b/samples/MvcSample/Views/Shared/MyView.cshtml @@ -12,7 +12,7 @@

Learn more »

-

Hello @Model.Name!

+

Hello @Model.Name! Happy @Model.Age birthday.

Getting started

diff --git a/src/Microsoft.AspNet.Mvc.Core/Filters/ActionFilterContext.cs b/src/Microsoft.AspNet.Mvc.Core/Filters/ActionFilterContext.cs index e469ba1628..cd8b6da53e 100644 --- a/src/Microsoft.AspNet.Mvc.Core/Filters/ActionFilterContext.cs +++ b/src/Microsoft.AspNet.Mvc.Core/Filters/ActionFilterContext.cs @@ -5,10 +5,13 @@ namespace Microsoft.AspNet.Mvc { public class ActionFilterContext { - public ActionFilterContext(ActionContext actionContext, IDictionary actionParameters) + public ActionFilterContext(ActionContext actionContext, + IDictionary actionParameters, + Type methodReturnType) { ActionContext = actionContext; ActionParameters = actionParameters; + MethodReturnType = methodReturnType; } public virtual IDictionary ActionParameters { get; private set; } @@ -17,6 +20,6 @@ namespace Microsoft.AspNet.Mvc public virtual Type MethodReturnType { get; private set; } - public virtual object Result { get; set; } + public virtual IActionResult Result { get; set; } } } diff --git a/src/Microsoft.AspNet.Mvc.Core/ReflectedActionDescriptorProvider.cs b/src/Microsoft.AspNet.Mvc.Core/ReflectedActionDescriptorProvider.cs index e56f47b65e..22253e9e99 100644 --- a/src/Microsoft.AspNet.Mvc.Core/ReflectedActionDescriptorProvider.cs +++ b/src/Microsoft.AspNet.Mvc.Core/ReflectedActionDescriptorProvider.cs @@ -46,9 +46,6 @@ namespace Microsoft.AspNet.Mvc var controllerAttributes = cd.ControllerTypeInfo.GetCustomAttributes(inherit: true).ToArray(); var filtersFromController = GetOrderedFilterAttributes(controllerAttributes); - bool allowAnonymous = IsAnonymous(controllerAttributes); - - bool allowAnonymous = IsAnonymous(controllerAttributes); foreach (var methodInfo in cd.ControllerTypeInfo.DeclaredMethods) diff --git a/src/Microsoft.AspNet.Mvc.Core/ReflectedActionInvoker.cs b/src/Microsoft.AspNet.Mvc.Core/ReflectedActionInvoker.cs index 4bf32dc8d6..2f659370e5 100644 --- a/src/Microsoft.AspNet.Mvc.Core/ReflectedActionInvoker.cs +++ b/src/Microsoft.AspNet.Mvc.Core/ReflectedActionInvoker.cs @@ -66,21 +66,40 @@ namespace Microsoft.AspNet.Mvc var parameterValues = await GetParameterValues(modelState); var authZFilters = context.AuthorizationFilters; - 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) + bool authZPassed; + if (authZFilters != null && authZFilters.Count > 0) { - var actionFilters = context.ActionFilters; + 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 + { + actionResult = authZContext.ActionResult ?? new HttpStatusCodeResult(401); + } + } + else + { + actionResult = null; + } + + if (actionResult == null) + { + var actionFilters = context.ActionFilters ?? new List(); var actionFilterContext = new ActionFilterContext(_actionContext, - new Dictionary()); + 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), @@ -93,18 +112,12 @@ namespace Microsoft.AspNet.Mvc await actionFilterPipeline.InvokeAsync(); - object actionReturnValue = method.Invoke(controller, null); - actionResult = _actionResultFactory.CreateActionResult(method.ReturnType, actionReturnValue, _actionContext); - } - else - { - actionResult = authZContext.ActionResult ?? new HttpStatusCodeResult(401); + actionResult = (IActionResult)actionFilterContext.Result; } } } - var actionResultFilters = context.ActionResultFilters; - await actionResult.ExecuteResultAsync(_actionContext); + var actionResultFilters = context.ActionResultFilters ?? new List(); var actionResultFilterContext = new ActionResultFilterContext(_actionContext, actionResult); var actionResultFilterEndPoint = new ActionResultFilterEndPoint(); actionResultFilters.Add(actionResultFilterEndPoint); diff --git a/src/Microsoft.AspNet.Mvc/MvcServices.cs b/src/Microsoft.AspNet.Mvc/MvcServices.cs index 4ad1ceade9..b5ed63ec43 100644 --- a/src/Microsoft.AspNet.Mvc/MvcServices.cs +++ b/src/Microsoft.AspNet.Mvc/MvcServices.cs @@ -51,7 +51,7 @@ namespace Microsoft.AspNet.Mvc yield return DescribeService, ReflectedActionDescriptorProvider>(configuration); yield return DescribeService, - ActionInvokerProvider>(configuration); + ReflectedActionInvokerProvider>(configuration); yield return DescribeService(configuration); yield return DescribeService(configuration);