// Copyright (c) Microsoft Corporation. All rights reserved. See License.txt in the project root for license information. using System.Collections.Generic; using System.ComponentModel; using System.Globalization; using System.Linq; using System.Reflection; using System.Threading; using Microsoft.Web.UnitTestUtil; using Moq; using Xunit; using Assert = Microsoft.TestCommon.AssertEx; namespace System.Web.Mvc.Test { [CLSCompliant(false)] public class ControllerActionInvokerTest { [Fact] public void CreateActionResultWithActionResultParameterReturnsParameterUnchanged() { // Arrange ControllerActionInvokerHelper invoker = new ControllerActionInvokerHelper(); ActionResult originalResult = new JsonResult(); // Act ActionResult returnedActionResult = invoker.PublicCreateActionResult(null, null, originalResult); // Assert Assert.Same(originalResult, returnedActionResult); } [Fact] public void CreateActionResultWithNullParameterReturnsEmptyResult() { // Arrange ControllerActionInvokerHelper invoker = new ControllerActionInvokerHelper(); // Act ActionResult returnedActionResult = invoker.PublicCreateActionResult(null, null, null); // Assert Assert.IsType(returnedActionResult); } [Fact] public void CreateActionResultWithObjectParameterReturnsContentResult() { // Arrange ControllerActionInvokerHelper invoker = new ControllerActionInvokerHelper(); object originalReturnValue = new CultureReflector(); // Act ActionResult returnedActionResult = invoker.PublicCreateActionResult(null, null, originalReturnValue); // Assert ContentResult contentResult = Assert.IsType(returnedActionResult); Assert.Equal("ivl", contentResult.Content); } [Fact] public void FindAction() { // Arrange EmptyController controller = new EmptyController(); ControllerContext controllerContext = GetControllerContext(controller); ControllerActionInvokerHelper helper = new ControllerActionInvokerHelper(); ActionDescriptor expectedAd = new Mock().Object; Mock mockCd = new Mock(); mockCd.Setup(cd => cd.FindAction(controllerContext, "someAction")).Returns(expectedAd); // Act ActionDescriptor returnedAd = helper.PublicFindAction(controllerContext, mockCd.Object, "someAction"); // Assert Assert.Equal(expectedAd, returnedAd); } [Fact] public void FindActionDoesNotMatchConstructor() { // FindActionMethod() shouldn't match special-named methods like type constructors. // Arrange Controller controller = new FindMethodController(); ControllerContext context = GetControllerContext(controller); ControllerDescriptor cd = new ReflectedControllerDescriptor(typeof(FindMethodController)); ControllerActionInvokerHelper helper = new ControllerActionInvokerHelper(); // Act ActionDescriptor ad = helper.PublicFindAction(context, cd, ".ctor"); ActionDescriptor ad2 = helper.PublicFindAction(context, cd, "FindMethodController"); // Assert Assert.Null(ad); Assert.Null(ad2); } [Fact] public void FindActionDoesNotMatchEvent() { // FindActionMethod() should skip methods that aren't publicly visible. // Arrange Controller controller = new FindMethodController(); ControllerContext context = GetControllerContext(controller); ControllerDescriptor cd = new ReflectedControllerDescriptor(typeof(FindMethodController)); ControllerActionInvokerHelper helper = new ControllerActionInvokerHelper(); // Act ActionDescriptor ad = helper.PublicFindAction(context, cd, "add_Event"); // Assert Assert.Null(ad); } [Fact] public void FindActionDoesNotMatchInternalMethod() { // FindActionMethod() should skip methods that aren't publicly visible. // Arrange Controller controller = new FindMethodController(); ControllerContext context = GetControllerContext(controller); ControllerDescriptor cd = new ReflectedControllerDescriptor(typeof(FindMethodController)); ControllerActionInvokerHelper helper = new ControllerActionInvokerHelper(); // Act ActionDescriptor ad = helper.PublicFindAction(context, cd, "InternalMethod"); // Assert Assert.Null(ad); } [Fact] public void FindActionDoesNotMatchMethodsDefinedOnControllerType() { // FindActionMethod() shouldn't match methods originally defined on the Controller type, e.g. Dispose(). // Arrange Controller controller = new BlankController(); ControllerDescriptor cd = new ReflectedControllerDescriptor(typeof(BlankController)); ControllerContext context = GetControllerContext(controller); ControllerActionInvokerHelper helper = new ControllerActionInvokerHelper(); var methods = typeof(Controller).GetMethods(BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic); // Act & Assert foreach (var method in methods) { bool wasFound = true; try { ActionDescriptor ad = helper.PublicFindAction(context, cd, method.Name); wasFound = (ad != null); } finally { Assert.False(wasFound, "FindAction() should return false for methods defined on the Controller class: " + method); } } } [Fact] public void FindActionDoesNotMatchMethodsDefinedOnObjectType() { // FindActionMethod() shouldn't match methods originally defined on the Object type, e.g. ToString(). // Arrange Controller controller = new FindMethodController(); ControllerContext context = GetControllerContext(controller); ControllerDescriptor cd = new ReflectedControllerDescriptor(typeof(FindMethodController)); ControllerActionInvokerHelper helper = new ControllerActionInvokerHelper(); // Act ActionDescriptor ad = helper.PublicFindAction(context, cd, "ToString"); // Assert Assert.Null(ad); } [Fact] public void FindActionDoesNotMatchNonActionMethod() { // FindActionMethod() should respect the [NonAction] attribute. // Arrange Controller controller = new FindMethodController(); ControllerContext context = GetControllerContext(controller); ControllerDescriptor cd = new ReflectedControllerDescriptor(typeof(FindMethodController)); ControllerActionInvokerHelper helper = new ControllerActionInvokerHelper(); // Act ActionDescriptor ad = helper.PublicFindAction(context, cd, "NonActionMethod"); // Assert Assert.Null(ad); } [Fact] public void FindActionDoesNotMatchOverriddenNonActionMethod() { // FindActionMethod() should trace the method's inheritance chain looking for the [NonAction] attribute. // Arrange Controller controller = new DerivedFindMethodController(); ControllerContext context = GetControllerContext(controller); ControllerDescriptor cd = new ReflectedControllerDescriptor(typeof(DerivedFindMethodController)); ControllerActionInvokerHelper helper = new ControllerActionInvokerHelper(); // Act ActionDescriptor ad = helper.PublicFindAction(context, cd, "InternalMethod"); // Assert Assert.Null(ad); } [Fact] public void FindActionDoesNotMatchPrivateMethod() { // FindActionMethod() should skip methods that aren't publicly visible. // Arrange Controller controller = new FindMethodController(); ControllerContext context = GetControllerContext(controller); ControllerDescriptor cd = new ReflectedControllerDescriptor(typeof(FindMethodController)); ControllerActionInvokerHelper helper = new ControllerActionInvokerHelper(); // Act ActionDescriptor ad = helper.PublicFindAction(context, cd, "PrivateMethod"); // Assert Assert.Null(ad); } [Fact] public void FindActionDoesNotMatchProperty() { // FindActionMethod() shouldn't match special-named methods like property getters. // Arrange Controller controller = new FindMethodController(); ControllerContext context = GetControllerContext(controller); ControllerDescriptor cd = new ReflectedControllerDescriptor(typeof(FindMethodController)); ControllerActionInvokerHelper helper = new ControllerActionInvokerHelper(); // Act ActionDescriptor ad = helper.PublicFindAction(context, cd, "get_Property"); // Assert Assert.Null(ad); } [Fact] public void FindActionDoesNotMatchProtectedMethod() { // FindActionMethod() should skip methods that aren't publicly visible. // Arrange Controller controller = new FindMethodController(); ControllerContext context = GetControllerContext(controller); ControllerDescriptor cd = new ReflectedControllerDescriptor(typeof(FindMethodController)); ControllerActionInvokerHelper helper = new ControllerActionInvokerHelper(); // Act ActionDescriptor ad = helper.PublicFindAction(context, cd, "ProtectedMethod"); // Assert Assert.Null(ad); } [Fact] public void FindActionIsCaseInsensitive() { // Arrange Controller controller = new FindMethodController(); ControllerContext context = GetControllerContext(controller); ControllerDescriptor cd = new ReflectedControllerDescriptor(typeof(FindMethodController)); MethodInfo expectedMethodInfo = typeof(FindMethodController).GetMethod("ValidActionMethod"); ControllerActionInvokerHelper helper = new ControllerActionInvokerHelper(); // Act ActionDescriptor ad1 = helper.PublicFindAction(context, cd, "validactionmethod"); ActionDescriptor ad2 = helper.PublicFindAction(context, cd, "VALIDACTIONMETHOD"); // Assert ReflectedActionDescriptor rad1 = Assert.IsType(ad1); Assert.Same(expectedMethodInfo, rad1.MethodInfo); ReflectedActionDescriptor rad2 = Assert.IsType(ad2); Assert.Same(expectedMethodInfo, rad2.MethodInfo); } [Fact] public void FindActionMatchesActionMethodWithClosedGenerics() { // FindActionMethod() should work with generic methods as long as there are no open types. // Arrange Controller controller = new GenericFindMethodController(); ControllerContext context = GetControllerContext(controller); ControllerDescriptor cd = new ReflectedControllerDescriptor(typeof(GenericFindMethodController)); MethodInfo expectedMethodInfo = typeof(GenericFindMethodController).GetMethod("ClosedGenericMethod"); ControllerActionInvokerHelper helper = new ControllerActionInvokerHelper(); // Act ActionDescriptor ad = helper.PublicFindAction(context, cd, "ClosedGenericMethod"); // Assert ReflectedActionDescriptor rad = Assert.IsType(ad); Assert.Same(expectedMethodInfo, rad.MethodInfo); } [Fact] public void FindActionMatchesNewActionMethodsHidingNonActionMethods() { // FindActionMethod() should stop looking for [NonAction] in the method's inheritance chain when it sees // that a method in a derived class hides the a method in the base class. // Arrange Controller controller = new DerivedFindMethodController(); ControllerContext context = GetControllerContext(controller); ControllerDescriptor cd = new ReflectedControllerDescriptor(typeof(DerivedFindMethodController)); MethodInfo expectedMethodInfo = typeof(DerivedFindMethodController).GetMethod("DerivedIsActionMethod"); ControllerActionInvokerHelper helper = new ControllerActionInvokerHelper(); // Act ActionDescriptor ad = helper.PublicFindAction(context, cd, "DerivedIsActionMethod"); // Assert ReflectedActionDescriptor rad = Assert.IsType(ad); Assert.Same(expectedMethodInfo, rad.MethodInfo); } [Fact] public void GetControllerDescriptor() { // Arrange EmptyController controller = new EmptyController(); ControllerContext controllerContext = GetControllerContext(controller); ControllerActionInvokerHelper helper = new ControllerActionInvokerHelper(); // Act ControllerDescriptor cd = helper.PublicGetControllerDescriptor(controllerContext); // Assert Assert.IsType(cd); Assert.Equal(typeof(EmptyController), cd.ControllerType); } [Fact] public void GetFiltersSplitsFilterObjectsIntoFilterInfo() { // Arrange IActionFilter actionFilter = new Mock().Object; IResultFilter resultFilter = new Mock().Object; IAuthorizationFilter authFilter = new Mock().Object; IExceptionFilter exFilter = new Mock().Object; object noneOfTheAbove = new object(); ControllerActionInvokerHelper invoker = new ControllerActionInvokerHelper(actionFilter, authFilter, exFilter, resultFilter, noneOfTheAbove); ControllerContext context = new ControllerContext(); ActionDescriptor descriptor = new Mock().Object; // Act FilterInfo result = invoker.PublicGetFilters(context, descriptor); // Assert Assert.Same(actionFilter, result.ActionFilters.Single()); Assert.Same(authFilter, result.AuthorizationFilters.Single()); Assert.Same(exFilter, result.ExceptionFilters.Single()); Assert.Same(resultFilter, result.ResultFilters.Single()); } [Fact] public void GetParameterValueAllowsAllSubpropertiesIfBindAttributeNotSpecified() { // Arrange CustomConverterController controller = new CustomConverterController(); ControllerContext controllerContext = GetControllerContext(controller); ControllerActionInvokerHelper helper = new ControllerActionInvokerHelper(); ParameterInfo paramWithoutBindAttribute = typeof(CustomConverterController).GetMethod("ParameterWithoutBindAttribute").GetParameters()[0]; ReflectedParameterDescriptor pd = new ReflectedParameterDescriptor(paramWithoutBindAttribute, new Mock().Object); // Act object valueWithoutBindAttribute = helper.PublicGetParameterValue(controllerContext, pd); // Assert Assert.Equal("foo=True&bar=True", valueWithoutBindAttribute); } [Fact] public void GetParameterValueResolvesConvertersInCorrectOrderOfPrecedence() { // Order of precedence: // 1. Attributes on the parameter itself // 2. Query the global converter provider // Arrange CustomConverterController controller = new CustomConverterController(); Dictionary values = new Dictionary { { "foo", "fooValue" } }; ControllerContext controllerContext = GetControllerContext(controller, values); controller.ControllerContext = controllerContext; ControllerActionInvokerHelper helper = new ControllerActionInvokerHelper(); ParameterInfo paramWithOneConverter = typeof(CustomConverterController).GetMethod("ParameterHasOneConverter").GetParameters()[0]; ReflectedParameterDescriptor pdOneConverter = new ReflectedParameterDescriptor(paramWithOneConverter, new Mock().Object); ParameterInfo paramWithNoConverters = typeof(CustomConverterController).GetMethod("ParameterHasNoConverters").GetParameters()[0]; ReflectedParameterDescriptor pdNoConverters = new ReflectedParameterDescriptor(paramWithNoConverters, new Mock().Object); // Act object valueWithOneConverter = helper.PublicGetParameterValue(controllerContext, pdOneConverter); object valueWithNoConverters = helper.PublicGetParameterValue(controllerContext, pdNoConverters); // Assert Assert.Equal("foo_String", valueWithOneConverter); Assert.Equal("fooValue", valueWithNoConverters); } [Fact] public void GetParameterValueRespectsBindAttribute() { // Arrange CustomConverterController controller = new CustomConverterController(); ControllerContext controllerContext = GetControllerContext(controller); ControllerActionInvokerHelper helper = new ControllerActionInvokerHelper(); ParameterInfo paramWithBindAttribute = typeof(CustomConverterController).GetMethod("ParameterHasBindAttribute").GetParameters()[0]; ReflectedParameterDescriptor pd = new ReflectedParameterDescriptor(paramWithBindAttribute, new Mock().Object); // Act object valueWithBindAttribute = helper.PublicGetParameterValue(controllerContext, pd); // Assert Assert.Equal("foo=True&bar=False", valueWithBindAttribute); } [Fact] public void GetParameterValueRespectsBindAttributePrefix() { // Arrange CustomConverterController controller = new CustomConverterController(); Dictionary values = new Dictionary { { "foo", "fooValue" }, { "bar", "barValue" } }; ControllerContext controllerContext = GetControllerContext(controller, values); controller.ControllerContext = controllerContext; ControllerActionInvokerHelper helper = new ControllerActionInvokerHelper(); ParameterInfo paramWithFieldPrefix = typeof(CustomConverterController).GetMethod("ParameterHasFieldPrefix").GetParameters()[0]; ReflectedParameterDescriptor pd = new ReflectedParameterDescriptor(paramWithFieldPrefix, new Mock().Object); // Act object parameterValue = helper.PublicGetParameterValue(controllerContext, pd); // Assert Assert.Equal("barValue", parameterValue); } [Fact] public void GetParameterValueRespectsBindAttributePrefixOnComplexType() { // Arrange CustomConverterController controller = new CustomConverterController(); Dictionary values = new Dictionary { { "intprop", "123" }, { "stringprop", "hello" } }; ControllerContext controllerContext = GetControllerContext(controller, values); controller.ControllerContext = controllerContext; ControllerActionInvokerHelper helper = new ControllerActionInvokerHelper(); ParameterInfo paramWithFieldPrefix = typeof(CustomConverterController).GetMethod("ParameterHasPrefixAndComplexType").GetParameters()[0]; ReflectedParameterDescriptor pd = new ReflectedParameterDescriptor(paramWithFieldPrefix, new Mock().Object); // Act MySimpleModel parameterValue = helper.PublicGetParameterValue(controllerContext, pd) as MySimpleModel; // Assert Assert.Null(parameterValue); } [Fact] public void GetParameterValueRespectsBindAttributeNullPrefix() { // Arrange CustomConverterController controller = new CustomConverterController(); Dictionary values = new Dictionary { { "foo", "fooValue" }, { "bar", "barValue" } }; ControllerContext controllerContext = GetControllerContext(controller, values); controller.ControllerContext = controllerContext; ControllerActionInvokerHelper helper = new ControllerActionInvokerHelper(); ParameterInfo paramWithFieldPrefix = typeof(CustomConverterController).GetMethod("ParameterHasNullFieldPrefix").GetParameters()[0]; ReflectedParameterDescriptor pd = new ReflectedParameterDescriptor(paramWithFieldPrefix, new Mock().Object); // Act object parameterValue = helper.PublicGetParameterValue(controllerContext, pd); // Assert Assert.Equal("fooValue", parameterValue); } [Fact] public void GetParameterValueRespectsBindAttributeNullPrefixOnComplexType() { // Arrange CustomConverterController controller = new CustomConverterController(); Dictionary values = new Dictionary { { "intprop", "123" }, { "stringprop", "hello" } }; ControllerContext controllerContext = GetControllerContext(controller, values); controller.ControllerContext = controllerContext; ControllerActionInvokerHelper helper = new ControllerActionInvokerHelper(); ParameterInfo paramWithFieldPrefix = typeof(CustomConverterController).GetMethod("ParameterHasNoPrefixAndComplexType").GetParameters()[0]; ReflectedParameterDescriptor pd = new ReflectedParameterDescriptor(paramWithFieldPrefix, new Mock().Object); // Act MySimpleModel parameterValue = helper.PublicGetParameterValue(controllerContext, pd) as MySimpleModel; // Assert Assert.NotNull(parameterValue); Assert.Equal(123, parameterValue.IntProp); Assert.Equal("hello", parameterValue.StringProp); } [Fact] public void GetParameterValueRespectsBindAttributeEmptyPrefix() { // Arrange CustomConverterController controller = new CustomConverterController(); Dictionary values = new Dictionary { { "foo", "fooValue" }, { "bar", "barValue" }, { "intprop", "123" }, { "stringprop", "hello" } }; ControllerContext controllerContext = GetControllerContext(controller, values); controller.ControllerContext = controllerContext; ControllerActionInvokerHelper helper = new ControllerActionInvokerHelper(); ParameterInfo paramWithFieldPrefix = typeof(CustomConverterController).GetMethod("ParameterHasEmptyFieldPrefix").GetParameters()[0]; ReflectedParameterDescriptor pd = new ReflectedParameterDescriptor(paramWithFieldPrefix, new Mock().Object); // Act MySimpleModel parameterValue = helper.PublicGetParameterValue(controllerContext, pd) as MySimpleModel; // Assert Assert.NotNull(parameterValue); Assert.Equal(123, parameterValue.IntProp); Assert.Equal("hello", parameterValue.StringProp); } [Fact] public void GetParameterValueRespectsDefaultValueAttribute() { // Arrange CustomConverterController controller = new CustomConverterController(); ControllerContext controllerContext = GetControllerContext(controller); ControllerActionInvokerHelper helper = new ControllerActionInvokerHelper(); controller.ValueProvider = new SimpleValueProvider(); ParameterInfo paramWithDefaultValueAttribute = typeof(CustomConverterController).GetMethod("ParameterHasDefaultValueAttribute").GetParameters()[0]; ReflectedParameterDescriptor pd = new ReflectedParameterDescriptor(paramWithDefaultValueAttribute, new Mock().Object); // Act object valueWithDefaultValueAttribute = helper.PublicGetParameterValue(controllerContext, pd); // Assert Assert.Equal(42, valueWithDefaultValueAttribute); } [Fact] public void GetParameterValueReturnsNullIfCannotConvertNonRequiredParameter() { // Arrange Dictionary dict = new Dictionary() { { "id", DateTime.Now } // cannot convert DateTime to Nullable }; var controller = new ParameterTestingController(); ControllerContext context = GetControllerContext(controller, dict); controller.ControllerContext = context; ControllerActionInvokerHelper helper = new ControllerActionInvokerHelper(); MethodInfo mi = typeof(ParameterTestingController).GetMethod("TakesNullableInt"); ParameterInfo[] pis = mi.GetParameters(); ReflectedParameterDescriptor pd = new ReflectedParameterDescriptor(pis[0], new Mock().Object); // Act object oValue = helper.PublicGetParameterValue(context, pd); // Assert Assert.Null(oValue); } [Fact] public void GetParameterValueReturnsNullIfNullableTypeValueNotFound() { // Arrange var controller = new ParameterTestingController(); ControllerContext context = GetControllerContext(controller); controller.ControllerContext = context; controller.ValueProvider = new SimpleValueProvider(); ControllerActionInvokerHelper helper = new ControllerActionInvokerHelper(); MethodInfo mi = typeof(ParameterTestingController).GetMethod("TakesNullableInt"); ParameterInfo[] pis = mi.GetParameters(); ReflectedParameterDescriptor pd = new ReflectedParameterDescriptor(pis[0], new Mock().Object); // Act object oValue = helper.PublicGetParameterValue(context, pd); // Assert Assert.Null(oValue); } [Fact] public void GetParameterValueReturnsNullIfReferenceTypeValueNotFound() { // Arrange var controller = new ParameterTestingController(); ControllerContext context = GetControllerContext(controller); controller.ControllerContext = context; controller.ValueProvider = new SimpleValueProvider(); ControllerActionInvokerHelper helper = new ControllerActionInvokerHelper(); MethodInfo mi = typeof(ParameterTestingController).GetMethod("Foo"); ParameterInfo[] pis = mi.GetParameters(); ReflectedParameterDescriptor pd = new ReflectedParameterDescriptor(pis[0], new Mock().Object); // Act object oValue = helper.PublicGetParameterValue(context, pd); // Assert Assert.Null(oValue); } [Fact] public void GetParameterValuesCallsGetParameterValue() { // Arrange ControllerBase controller = new ParameterTestingController(); IDictionary dict = new Dictionary(); ControllerContext context = GetControllerContext(controller); MethodInfo mi = typeof(ParameterTestingController).GetMethod("Foo"); ReflectedActionDescriptor ad = new ReflectedActionDescriptor(mi, "Foo", new Mock().Object); ParameterDescriptor[] pds = ad.GetParameters(); Mock mockHelper = new Mock() { CallBase = true }; mockHelper.Setup(h => h.PublicGetParameterValue(context, pds[0])).Returns("Myfoo").Verifiable(); mockHelper.Setup(h => h.PublicGetParameterValue(context, pds[1])).Returns("Mybar").Verifiable(); mockHelper.Setup(h => h.PublicGetParameterValue(context, pds[2])).Returns("Mybaz").Verifiable(); ControllerActionInvokerHelper helper = mockHelper.Object; // Act IDictionary parameters = helper.PublicGetParameterValues(context, ad); // Assert Assert.Equal(3, parameters.Count); Assert.Equal("Myfoo", parameters["foo"]); Assert.Equal("Mybar", parameters["bar"]); Assert.Equal("Mybaz", parameters["baz"]); mockHelper.Verify(); } [Fact] public void GetParameterValuesReturnsEmptyDictionaryForParameterlessMethod() { // Arrange var controller = new ParameterTestingController(); ControllerContext context = GetControllerContext(controller); ControllerActionInvokerHelper helper = new ControllerActionInvokerHelper(); MethodInfo mi = typeof(ParameterTestingController).GetMethod("Parameterless"); ReflectedActionDescriptor ad = new ReflectedActionDescriptor(mi, "Parameterless", new Mock().Object); // Act IDictionary parameters = helper.PublicGetParameterValues(context, ad); // Assert Assert.Empty(parameters); } [Fact] public void GetParameterValuesReturnsValuesForParametersInOrder() { // We need to hook into GetParameterValue() to make sure that GetParameterValues() is calling it. // Arrange var controller = new ParameterTestingController(); Dictionary dict = new Dictionary() { { "foo", "MyFoo" }, { "bar", "MyBar" }, { "baz", "MyBaz" } }; ControllerContext context = GetControllerContext(controller, dict); controller.ControllerContext = context; ControllerActionInvokerHelper helper = new ControllerActionInvokerHelper(); MethodInfo mi = typeof(ParameterTestingController).GetMethod("Foo"); ReflectedActionDescriptor ad = new ReflectedActionDescriptor(mi, "Foo", new Mock().Object); // Act IDictionary parameters = helper.PublicGetParameterValues(context, ad); // Assert Assert.Equal(3, parameters.Count); Assert.Equal("MyFoo", parameters["foo"]); Assert.Equal("MyBar", parameters["bar"]); Assert.Equal("MyBaz", parameters["baz"]); } [Fact] public void GetParameterValueUsesControllerValueProviderAsValueProvider() { // Arrange Dictionary values = new Dictionary() { { "foo", "fooValue" } }; CustomConverterController controller = new CustomConverterController(); ControllerContext controllerContext = GetControllerContext(controller, values); ControllerActionInvokerHelper helper = new ControllerActionInvokerHelper(); ParameterInfo parameter = typeof(CustomConverterController).GetMethod("ParameterHasNoConverters").GetParameters()[0]; ReflectedParameterDescriptor pd = new ReflectedParameterDescriptor(parameter, new Mock().Object); // Act object parameterValue = helper.PublicGetParameterValue(controllerContext, pd); // Assert Assert.Equal("fooValue", parameterValue); } [Fact] public void InvokeAction() { // Arrange ControllerBase controller = new Mock().Object; ControllerContext context = GetControllerContext(controller); ControllerDescriptor cd = new Mock().Object; ActionDescriptor ad = new Mock().Object; FilterInfo filterInfo = new FilterInfo(); IDictionary parameters = new Dictionary(); MethodInfo methodInfo = typeof(object).GetMethod("ToString"); ActionResult actionResult = new EmptyResult(); ActionExecutedContext postContext = new ActionExecutedContext(context, ad, false /* canceled */, null /* exception */) { Result = actionResult }; AuthorizationContext authContext = new AuthorizationContext(); Mock mockHelper = new Mock() { CallBase = true }; mockHelper.Setup(h => h.PublicGetControllerDescriptor(context)).Returns(cd).Verifiable(); mockHelper.Setup(h => h.PublicFindAction(context, cd, "SomeMethod")).Returns(ad).Verifiable(); mockHelper.Setup(h => h.PublicGetFilters(context, ad)).Returns(filterInfo).Verifiable(); mockHelper.Setup(h => h.PublicInvokeAuthorizationFilters(context, filterInfo.AuthorizationFilters, ad)).Returns(authContext).Verifiable(); mockHelper.Setup(h => h.PublicGetParameterValues(context, ad)).Returns(parameters).Verifiable(); mockHelper.Setup(h => h.PublicInvokeActionMethodWithFilters(context, filterInfo.ActionFilters, ad, parameters)).Returns(postContext).Verifiable(); mockHelper.Setup(h => h.PublicInvokeActionResultWithFilters(context, filterInfo.ResultFilters, actionResult)).Returns((ResultExecutedContext)null).Verifiable(); ControllerActionInvokerHelper helper = mockHelper.Object; // Act bool retVal = helper.InvokeAction(context, "SomeMethod"); Assert.True(retVal); mockHelper.Verify(); } [Fact] public void InvokeActionCallsValidateRequestIfAsked() { // Arrange ControllerBase controller = new Mock().Object; controller.ValidateRequest = true; bool validateInputWasCalled = false; ControllerContext context = GetControllerContext(controller, null, validateInputCallback: () => { validateInputWasCalled = true; }); ControllerDescriptor cd = new Mock().Object; ActionDescriptor ad = new Mock().Object; FilterInfo filterInfo = new FilterInfo(); AuthorizationContext authContext = new AuthorizationContext(); Mock mockHelper = new Mock(); mockHelper.CallBase = true; mockHelper.Setup(h => h.PublicGetControllerDescriptor(context)).Returns(cd).Verifiable(); mockHelper.Setup(h => h.PublicFindAction(context, cd, "SomeMethod")).Returns(ad).Verifiable(); mockHelper.Setup(h => h.PublicGetFilters(context, ad)).Returns(filterInfo).Verifiable(); mockHelper.Setup(h => h.PublicInvokeAuthorizationFilters(context, filterInfo.AuthorizationFilters, ad)).Returns(authContext).Verifiable(); ControllerActionInvokerHelper helper = mockHelper.Object; // Act helper.InvokeAction(context, "SomeMethod"); // Assert Assert.True(validateInputWasCalled); mockHelper.Verify(); } [Fact] public void InvokeActionDoesNotCallValidateRequestForChildActions() { // Arrange ControllerBase controller = new Mock().Object; controller.ValidateRequest = true; ControllerContext context = GetControllerContext(controller, null); Mock.Get(context).SetupGet(c => c.IsChildAction).Returns(true); ControllerDescriptor cd = new Mock().Object; ActionDescriptor ad = new Mock().Object; FilterInfo filterInfo = new FilterInfo(); AuthorizationContext authContext = new AuthorizationContext(); Mock mockHelper = new Mock(); mockHelper.CallBase = true; mockHelper.Setup(h => h.PublicGetControllerDescriptor(context)).Returns(cd).Verifiable(); mockHelper.Setup(h => h.PublicFindAction(context, cd, "SomeMethod")).Returns(ad).Verifiable(); mockHelper.Setup(h => h.PublicGetFilters(context, ad)).Returns(filterInfo).Verifiable(); mockHelper.Setup(h => h.PublicInvokeAuthorizationFilters(context, filterInfo.AuthorizationFilters, ad)).Returns(authContext).Verifiable(); ControllerActionInvokerHelper helper = mockHelper.Object; // Act helper.InvokeAction(context, "SomeMethod"); // No exception thrown // Assert mockHelper.Verify(); } [Fact] public void InvokeActionMethodFilterWhereContinuationThrowsExceptionAndIsHandled() { // Arrange List actions = new List(); MethodInfo mi = typeof(object).GetMethod("ToString"); Dictionary parameters = new Dictionary(); Exception exception = new Exception(); ActionDescriptor action = new Mock().Object; ActionFilterImpl filter = new ActionFilterImpl() { OnActionExecutingImpl = delegate(ActionExecutingContext filterContext) { actions.Add("OnActionExecuting"); }, OnActionExecutedImpl = delegate(ActionExecutedContext filterContext) { actions.Add("OnActionExecuted"); Assert.Same(exception, filterContext.Exception); Assert.Same(action, filterContext.ActionDescriptor); Assert.False(filterContext.ExceptionHandled); filterContext.ExceptionHandled = true; } }; Func continuation = delegate { actions.Add("Continuation"); throw exception; }; ActionExecutingContext context = new ActionExecutingContext(GetControllerContext(new EmptyController()), action, parameters); // Act ActionExecutedContext result = ControllerActionInvoker.InvokeActionMethodFilter(filter, context, continuation); // Assert Assert.Equal(3, actions.Count); Assert.Equal("OnActionExecuting", actions[0]); Assert.Equal("Continuation", actions[1]); Assert.Equal("OnActionExecuted", actions[2]); Assert.Same(exception, result.Exception); Assert.Same(action, result.ActionDescriptor); Assert.True(result.ExceptionHandled); } [Fact] public void InvokeActionMethodFilterWhereContinuationThrowsExceptionAndIsNotHandled() { // Arrange List actions = new List(); Dictionary parameters = new Dictionary(); ActionDescriptor action = new Mock().Object; ActionFilterImpl filter = new ActionFilterImpl() { OnActionExecutingImpl = delegate(ActionExecutingContext filterContext) { actions.Add("OnActionExecuting"); }, OnActionExecutedImpl = delegate(ActionExecutedContext filterContext) { Assert.NotNull(filterContext.Exception); Assert.Equal("Some exception message.", filterContext.Exception.Message); Assert.Same(action, filterContext.ActionDescriptor); actions.Add("OnActionExecuted"); } }; Func continuation = delegate { actions.Add("Continuation"); throw new Exception("Some exception message."); }; ActionExecutingContext context = new ActionExecutingContext(GetControllerContext(new EmptyController()), action, parameters); // Act & Assert Assert.Throws( delegate { ControllerActionInvoker.InvokeActionMethodFilter(filter, context, continuation); }, "Some exception message."); Assert.Equal(3, actions.Count); Assert.Equal("OnActionExecuting", actions[0]); Assert.Equal("Continuation", actions[1]); Assert.Equal("OnActionExecuted", actions[2]); } [Fact] public void InvokeActionMethodFilterWhereContinuationThrowsThreadAbortException() { // Arrange List actions = new List(); ActionResult actionResult = new EmptyResult(); ActionDescriptor action = new Mock().Object; ActionFilterImpl filter = new ActionFilterImpl() { OnActionExecutingImpl = delegate(ActionExecutingContext filterContext) { actions.Add("OnActionExecuting"); }, OnActionExecutedImpl = delegate(ActionExecutedContext filterContext) { Thread.ResetAbort(); actions.Add("OnActionExecuted"); Assert.Null(filterContext.Exception); Assert.False(filterContext.ExceptionHandled); Assert.Same(action, filterContext.ActionDescriptor); } }; Func continuation = delegate { actions.Add("Continuation"); Thread.CurrentThread.Abort(); return null; }; ActionExecutingContext context = new ActionExecutingContext(new Mock().Object, action, new Dictionary()); // Act & Assert Assert.Throws( delegate { ControllerActionInvoker.InvokeActionMethodFilter(filter, context, continuation); }, "Thread was being aborted."); Assert.Equal(3, actions.Count); Assert.Equal("OnActionExecuting", actions[0]); Assert.Equal("Continuation", actions[1]); Assert.Equal("OnActionExecuted", actions[2]); } [Fact] public void InvokeActionMethodFilterWhereOnActionExecutingCancels() { // Arrange bool wasCalled = false; ActionDescriptor ad = new Mock().Object; Dictionary parameters = new Dictionary(); ActionResult actionResult = new EmptyResult(); ActionDescriptor action = new Mock().Object; ActionFilterImpl filter = new ActionFilterImpl() { OnActionExecutingImpl = delegate(ActionExecutingContext filterContext) { Assert.False(wasCalled); wasCalled = true; filterContext.Result = actionResult; }, }; Func continuation = delegate { Assert.True(false, "The continuation should not be called."); return null; }; ActionExecutingContext context = new ActionExecutingContext(GetControllerContext(new EmptyController()), action, parameters); // Act ActionExecutedContext result = ControllerActionInvoker.InvokeActionMethodFilter(filter, context, continuation); // Assert Assert.True(wasCalled); Assert.Null(result.Exception); Assert.True(result.Canceled); Assert.Same(actionResult, result.Result); Assert.Same(action, result.ActionDescriptor); } [Fact] public void InvokeActionMethodFilterWithNormalControlFlow() { // Arrange List actions = new List(); Dictionary parameters = new Dictionary(); ActionDescriptor action = new Mock().Object; ActionExecutingContext preContext = new ActionExecutingContext(GetControllerContext(new EmptyController()), action, parameters); Mock mockPostContext = new Mock(); ActionFilterImpl filter = new ActionFilterImpl() { OnActionExecutingImpl = delegate(ActionExecutingContext filterContext) { Assert.Same(parameters, filterContext.ActionParameters); Assert.Null(filterContext.Result); actions.Add("OnActionExecuting"); }, OnActionExecutedImpl = delegate(ActionExecutedContext filterContext) { Assert.Equal(mockPostContext.Object, filterContext); actions.Add("OnActionExecuted"); } }; Func continuation = delegate { actions.Add("Continuation"); return mockPostContext.Object; }; // Act ActionExecutedContext result = ControllerActionInvoker.InvokeActionMethodFilter(filter, preContext, continuation); // Assert Assert.Equal(3, actions.Count); Assert.Equal("OnActionExecuting", actions[0]); Assert.Equal("Continuation", actions[1]); Assert.Equal("OnActionExecuted", actions[2]); Assert.Same(result, mockPostContext.Object); } [Fact] public void InvokeActionInvokesExceptionFiltersAndExecutesResultIfExceptionHandled() { // Arrange ControllerBase controller = new Mock().Object; ControllerContext context = GetControllerContext(controller); ControllerDescriptor cd = new Mock().Object; ActionDescriptor ad = new Mock().Object; FilterInfo filterInfo = new FilterInfo(); Exception exception = new Exception(); ActionResult actionResult = new EmptyResult(); ExceptionContext exContext = new ExceptionContext(context, exception) { ExceptionHandled = true, Result = actionResult }; Mock mockHelper = new Mock() { CallBase = true }; mockHelper.Setup(h => h.PublicGetControllerDescriptor(context)).Returns(cd).Verifiable(); mockHelper.Setup(h => h.PublicFindAction(context, cd, "SomeMethod")).Returns(ad).Verifiable(); mockHelper.Setup(h => h.PublicGetFilters(context, ad)).Returns(filterInfo).Verifiable(); mockHelper.Setup(h => h.PublicInvokeAuthorizationFilters(context, filterInfo.AuthorizationFilters, ad)).Throws(exception).Verifiable(); mockHelper.Setup(h => h.PublicInvokeExceptionFilters(context, filterInfo.ExceptionFilters, exception)).Returns(exContext).Verifiable(); mockHelper.Setup(h => h.PublicInvokeActionResult(context, actionResult)).Verifiable(); ControllerActionInvokerHelper helper = mockHelper.Object; // Act bool retVal = helper.InvokeAction(context, "SomeMethod"); Assert.True(retVal); mockHelper.Verify(); } [Fact] public void InvokeActionInvokesExceptionFiltersAndRethrowsExceptionIfNotHandled() { // Arrange ControllerBase controller = new Mock().Object; ControllerContext context = GetControllerContext(controller); ControllerDescriptor cd = new Mock().Object; ActionDescriptor ad = new Mock().Object; FilterInfo filterInfo = new FilterInfo(); Exception exception = new Exception(); ExceptionContext exContext = new ExceptionContext(context, exception); Mock mockHelper = new Mock() { CallBase = true }; mockHelper.Setup(h => h.PublicGetControllerDescriptor(context)).Returns(cd).Verifiable(); mockHelper.Setup(h => h.PublicFindAction(context, cd, "SomeMethod")).Returns(ad).Verifiable(); mockHelper.Setup(h => h.PublicGetFilters(context, ad)).Returns(filterInfo).Verifiable(); mockHelper.Setup(h => h.PublicInvokeAuthorizationFilters(context, filterInfo.AuthorizationFilters, ad)).Throws(exception).Verifiable(); mockHelper.Setup(h => h.PublicInvokeExceptionFilters(context, filterInfo.ExceptionFilters, exception)).Returns(exContext).Verifiable(); mockHelper.Setup(h => h.PublicInvokeActionResult(context, It.IsAny())).Callback(delegate { Assert.True(false, "InvokeActionResult() shouldn't be called if the exception was unhandled by filters."); }); ControllerActionInvokerHelper helper = mockHelper.Object; // Act Exception thrownException = Assert.Throws( delegate { helper.InvokeAction(context, "SomeMethod"); }); // Assert Assert.Same(exception, thrownException); mockHelper.Verify(); } [Fact] public void InvokeActionInvokesResultIfAuthorizationFilterReturnsResult() { // Arrange ControllerBase controller = new Mock().Object; ControllerContext context = GetControllerContext(controller); ControllerDescriptor cd = new Mock().Object; ActionDescriptor ad = new Mock().Object; FilterInfo filterInfo = new FilterInfo(); ActionResult actionResult = new EmptyResult(); ActionExecutedContext postContext = new ActionExecutedContext(context, ad, false /* canceled */, null /* exception */) { Result = actionResult }; AuthorizationContext authContext = new AuthorizationContext() { Result = actionResult }; Mock mockHelper = new Mock() { CallBase = true }; mockHelper.Setup(h => h.PublicGetControllerDescriptor(context)).Returns(cd).Verifiable(); mockHelper.Setup(h => h.PublicFindAction(context, cd, "SomeMethod")).Returns(ad).Verifiable(); mockHelper.Setup(h => h.PublicGetFilters(context, ad)).Returns(filterInfo).Verifiable(); mockHelper.Setup(h => h.PublicInvokeAuthorizationFilters(context, filterInfo.AuthorizationFilters, ad)).Returns(authContext).Verifiable(); mockHelper.Setup(h => h.PublicInvokeActionResult(context, actionResult)).Verifiable(); ControllerActionInvokerHelper helper = mockHelper.Object; // Act bool retVal = helper.InvokeAction(context, "SomeMethod"); Assert.True(retVal); mockHelper.Verify(); } [Fact] public void InvokeActionMethod() { // Arrange EmptyController controller = new EmptyController(); ControllerContext controllerContext = GetControllerContext(controller); Dictionary parameters = new Dictionary(); ActionResult expectedResult = new Mock().Object; Mock mockAd = new Mock(); mockAd.Setup(ad => ad.Execute(controllerContext, parameters)).Returns("hello world"); Mock mockHelper = new Mock() { CallBase = true }; mockHelper.Setup(h => h.PublicCreateActionResult(controllerContext, mockAd.Object, "hello world")).Returns(expectedResult); ControllerActionInvokerHelper helper = mockHelper.Object; // Act ActionResult returnedResult = helper.PublicInvokeActionMethod(controllerContext, mockAd.Object, parameters); // Assert Assert.Same(expectedResult, returnedResult); } [Fact] public void InvokeActionMethodWithFiltersOrdersFiltersCorrectly() { // Arrange List actions = new List(); Dictionary parameters = new Dictionary(); ActionResult actionResult = new EmptyResult(); ActionFilterImpl filter1 = new ActionFilterImpl() { OnActionExecutingImpl = delegate(ActionExecutingContext filterContext) { actions.Add("OnActionExecuting1"); }, OnActionExecutedImpl = delegate(ActionExecutedContext filterContext) { actions.Add("OnActionExecuted1"); } }; ActionFilterImpl filter2 = new ActionFilterImpl() { OnActionExecutingImpl = delegate(ActionExecutingContext filterContext) { actions.Add("OnActionExecuting2"); }, OnActionExecutedImpl = delegate(ActionExecutedContext filterContext) { actions.Add("OnActionExecuted2"); } }; Func continuation = delegate { actions.Add("Continuation"); return new EmptyResult(); }; ControllerBase controller = new ContinuationController(continuation); ControllerContext context = GetControllerContext(controller); ActionDescriptor actionDescriptor = new ReflectedActionDescriptor(ContinuationController.GoMethod, "someName", new Mock().Object); ControllerActionInvokerHelper helper = new ControllerActionInvokerHelper(); List filters = new List() { filter1, filter2 }; // Act helper.PublicInvokeActionMethodWithFilters(context, filters, actionDescriptor, parameters); // Assert Assert.Equal(5, actions.Count); Assert.Equal("OnActionExecuting1", actions[0]); Assert.Equal("OnActionExecuting2", actions[1]); Assert.Equal("Continuation", actions[2]); Assert.Equal("OnActionExecuted2", actions[3]); Assert.Equal("OnActionExecuted1", actions[4]); } [Fact] public void InvokeActionMethodWithFiltersPassesArgumentsCorrectly() { // Arrange bool wasCalled = false; MethodInfo mi = ContinuationController.GoMethod; Dictionary parameters = new Dictionary(); ActionResult actionResult = new EmptyResult(); ActionFilterImpl filter = new ActionFilterImpl() { OnActionExecutingImpl = delegate(ActionExecutingContext filterContext) { Assert.Same(parameters, filterContext.ActionParameters); Assert.False(wasCalled); wasCalled = true; filterContext.Result = actionResult; } }; Func continuation = delegate { Assert.True(false, "Continuation should not be called."); return null; }; ControllerBase controller = new ContinuationController(continuation); ControllerContext context = GetControllerContext(controller); ControllerActionInvokerHelper helper = new ControllerActionInvokerHelper(); ActionDescriptor actionDescriptor = new ReflectedActionDescriptor(ContinuationController.GoMethod, "someName", new Mock().Object); List filters = new List() { filter }; // Act ActionExecutedContext result = helper.PublicInvokeActionMethodWithFilters(context, filters, actionDescriptor, parameters); // Assert Assert.True(wasCalled); Assert.Null(result.Exception); Assert.False(result.ExceptionHandled); Assert.Same(actionResult, result.Result); Assert.Same(actionDescriptor, result.ActionDescriptor); } [Fact] public void InvokeActionPropagatesThreadAbortException() { // Arrange ControllerBase controller = new Mock().Object; ControllerContext context = GetControllerContext(controller); ControllerDescriptor cd = new Mock().Object; ActionDescriptor ad = new Mock().Object; FilterInfo filterInfo = new FilterInfo(); ActionResult actionResult = new EmptyResult(); ActionExecutedContext postContext = new ActionExecutedContext(context, ad, false /* canceled */, null /* exception */) { Result = actionResult }; AuthorizationContext authContext = new AuthorizationContext() { Result = actionResult }; Mock mockHelper = new Mock() { CallBase = true }; mockHelper.Setup(h => h.PublicGetControllerDescriptor(context)).Returns(cd).Verifiable(); mockHelper.Setup(h => h.PublicFindAction(context, cd, "SomeMethod")).Returns(ad).Verifiable(); mockHelper.Setup(h => h.PublicGetFilters(context, ad)).Returns(filterInfo).Verifiable(); mockHelper .Setup(h => h.PublicInvokeAuthorizationFilters(context, filterInfo.AuthorizationFilters, ad)) .Returns( delegate(ControllerContext cc, IList f, ActionDescriptor a) { Thread.CurrentThread.Abort(); return null; }); ControllerActionInvokerHelper helper = mockHelper.Object; bool wasAborted = false; // Act try { helper.InvokeAction(context, "SomeMethod"); } catch (ThreadAbortException) { wasAborted = true; Thread.ResetAbort(); } // Assert Assert.True(wasAborted); mockHelper.Verify(); } [Fact] public void InvokeActionResultWithFiltersPassesSameContextObjectToInnerFilters() { // Arrange ControllerBase controller = new Mock().Object; ControllerContext context = GetControllerContext(controller); ResultExecutingContext storedContext = null; ActionResult result = new EmptyResult(); List filters = new List() { new ActionFilterImpl() { OnResultExecutingImpl = delegate(ResultExecutingContext ctx) { storedContext = ctx; }, OnResultExecutedImpl = delegate { } }, new ActionFilterImpl() { OnResultExecutingImpl = delegate(ResultExecutingContext ctx) { Assert.Same(storedContext, ctx); }, OnResultExecutedImpl = delegate { } }, }; ControllerActionInvokerHelper helper = new ControllerActionInvokerHelper(); // Act ResultExecutedContext postContext = helper.PublicInvokeActionResultWithFilters(context, filters, result); // Assert Assert.Same(result, postContext.Result); } [Fact] public void InvokeActionReturnsFalseIfMethodNotFound() { // Arrange var controller = new BlankController(); ControllerContext context = GetControllerContext(controller); ControllerActionInvoker invoker = new ControllerActionInvoker(); // Act bool retVal = invoker.InvokeAction(context, "foo"); // Assert Assert.False(retVal); } [Fact] public void InvokeActionThrowsIfControllerContextIsNull() { // Arrange ControllerActionInvoker invoker = new ControllerActionInvoker(); // Act & Assert Assert.ThrowsArgumentNull( delegate { invoker.InvokeAction(null, "actionName"); }, "controllerContext"); } [Fact] public void InvokeActionWithEmptyActionNameThrows() { // Arrange var controller = new BasicMethodInvokeController(); ControllerContext context = GetControllerContext(controller); ControllerActionInvoker invoker = new ControllerActionInvoker(); // Act & Assert Assert.ThrowsArgumentNullOrEmpty( delegate { invoker.InvokeAction(context, String.Empty); }, "actionName"); } [Fact] public void InvokeActionWithNullActionNameThrows() { // Arrange var controller = new BasicMethodInvokeController(); ControllerContext context = GetControllerContext(controller); ControllerActionInvoker invoker = new ControllerActionInvoker(); // Act & Assert Assert.ThrowsArgumentNullOrEmpty( delegate { invoker.InvokeAction(context, null /* actionName */); }, "actionName"); } [Fact] public void InvokeActionWithResultExceptionInvokesExceptionFiltersAndExecutesResultIfExceptionHandled() { // Arrange ControllerBase controller = new Mock().Object; ControllerContext context = GetControllerContext(controller); ControllerDescriptor cd = new Mock().Object; ActionDescriptor ad = new Mock().Object; IDictionary parameters = new Dictionary(); FilterInfo filterInfo = new FilterInfo(); AuthorizationContext authContext = new AuthorizationContext(); Exception exception = new Exception(); ActionResult actionResult = new EmptyResult(); ActionExecutedContext postContext = new ActionExecutedContext(context, ad, false /* canceled */, null /* exception */) { Result = actionResult }; ExceptionContext exContext = new ExceptionContext(context, exception) { ExceptionHandled = true, Result = actionResult }; Mock mockHelper = new Mock() { CallBase = true }; mockHelper.Setup(h => h.PublicGetControllerDescriptor(context)).Returns(cd).Verifiable(); mockHelper.Setup(h => h.PublicFindAction(context, cd, "SomeMethod")).Returns(ad).Verifiable(); mockHelper.Setup(h => h.PublicGetFilters(context, ad)).Returns(filterInfo).Verifiable(); mockHelper.Setup(h => h.PublicInvokeAuthorizationFilters(context, filterInfo.AuthorizationFilters, ad)).Returns(authContext).Verifiable(); mockHelper.Setup(h => h.PublicGetParameterValues(context, ad)).Returns(parameters).Verifiable(); mockHelper.Setup(h => h.PublicInvokeActionMethodWithFilters(context, filterInfo.ActionFilters, ad, parameters)).Returns(postContext).Verifiable(); mockHelper.Setup(h => h.PublicInvokeActionResultWithFilters(context, filterInfo.ResultFilters, actionResult)).Throws(exception).Verifiable(); mockHelper.Setup(h => h.PublicInvokeExceptionFilters(context, filterInfo.ExceptionFilters, exception)).Returns(exContext).Verifiable(); mockHelper.Setup(h => h.PublicInvokeActionResult(context, actionResult)).Verifiable(); ControllerActionInvokerHelper helper = mockHelper.Object; // Act bool retVal = helper.InvokeAction(context, "SomeMethod"); Assert.True(retVal, "InvokeAction() should return True on success."); mockHelper.Verify(); } [Fact] public void InvokeAuthorizationFilters() { // Arrange ControllerBase controller = new Mock().Object; ActionDescriptor ad = new Mock().Object; ControllerContext controllerContext = GetControllerContext(controller); ControllerActionInvokerHelper helper = new ControllerActionInvokerHelper(); List callQueue = new List(); AuthorizationFilterHelper filter1 = new AuthorizationFilterHelper(callQueue); AuthorizationFilterHelper filter2 = new AuthorizationFilterHelper(callQueue); IAuthorizationFilter[] filters = new IAuthorizationFilter[] { filter1, filter2 }; // Act AuthorizationContext postContext = helper.PublicInvokeAuthorizationFilters(controllerContext, filters, ad); // Assert Assert.Equal(ad, postContext.ActionDescriptor); Assert.Equal(2, callQueue.Count); Assert.Same(filter1, callQueue[0]); Assert.Same(filter2, callQueue[1]); } [Fact] public void InvokeAuthorizationFiltersStopsExecutingIfResultProvided() { // Arrange ControllerBase controller = new Mock().Object; ActionDescriptor ad = new Mock().Object; ControllerContext controllerContext = GetControllerContext(controller); ControllerActionInvokerHelper helper = new ControllerActionInvokerHelper(); ActionResult result = new EmptyResult(); List callQueue = new List(); AuthorizationFilterHelper filter1 = new AuthorizationFilterHelper(callQueue) { ShortCircuitResult = result }; AuthorizationFilterHelper filter2 = new AuthorizationFilterHelper(callQueue); IAuthorizationFilter[] filters = new IAuthorizationFilter[] { filter1, filter2 }; // Act AuthorizationContext postContext = helper.PublicInvokeAuthorizationFilters(controllerContext, filters, ad); // Assert Assert.Equal(ad, postContext.ActionDescriptor); Assert.Same(result, postContext.Result); Assert.Single(callQueue); Assert.Same(filter1, callQueue[0]); } [Fact] public void InvokeExceptionFilters() { // Arrange ControllerBase controller = new Mock().Object; Exception exception = new Exception(); ControllerContext controllerContext = GetControllerContext(controller); ControllerActionInvokerHelper helper = new ControllerActionInvokerHelper(); List callQueue = new List(); ExceptionFilterHelper filter1 = new ExceptionFilterHelper(callQueue); ExceptionFilterHelper filter2 = new ExceptionFilterHelper(callQueue); IExceptionFilter[] filters = new IExceptionFilter[] { filter1, filter2 }; // Act ExceptionContext postContext = helper.PublicInvokeExceptionFilters(controllerContext, filters, exception); // Assert Assert.Same(exception, postContext.Exception); Assert.False(postContext.ExceptionHandled); Assert.Same(filter1.ContextPassed, filter2.ContextPassed); Assert.Equal(2, callQueue.Count); Assert.Same(filter2, callQueue[0]); // Exception filters are executed in reverse order Assert.Same(filter1, callQueue[1]); } [Fact] public void InvokeExceptionFiltersContinuesExecutingIfExceptionHandled() { // Arrange ControllerBase controller = new Mock().Object; Exception exception = new Exception(); ControllerContext controllerContext = GetControllerContext(controller); ControllerActionInvokerHelper helper = new ControllerActionInvokerHelper(); List callQueue = new List(); ExceptionFilterHelper filter1 = new ExceptionFilterHelper(callQueue) { ShouldHandleException = true }; ExceptionFilterHelper filter2 = new ExceptionFilterHelper(callQueue); IExceptionFilter[] filters = new IExceptionFilter[] { filter1, filter2 }; // Act ExceptionContext postContext = helper.PublicInvokeExceptionFilters(controllerContext, filters, exception); // Assert Assert.Same(exception, postContext.Exception); Assert.True(postContext.ExceptionHandled); Assert.Same(filter1.ContextPassed, filter2.ContextPassed); Assert.Equal(2, callQueue.Count); Assert.Same(filter2, callQueue[0]); // Exception filters are executed in reverse order Assert.Same(filter1, callQueue[1]); } [Fact] public void InvokeResultFiltersOrdersFiltersCorrectly() { // Arrange List actions = new List(); ActionFilterImpl filter1 = new ActionFilterImpl() { OnResultExecutingImpl = delegate(ResultExecutingContext filterContext) { actions.Add("OnResultExecuting1"); }, OnResultExecutedImpl = delegate(ResultExecutedContext filterContext) { actions.Add("OnResultExecuted1"); } }; ActionFilterImpl filter2 = new ActionFilterImpl() { OnResultExecutingImpl = delegate(ResultExecutingContext filterContext) { actions.Add("OnResultExecuting2"); }, OnResultExecutedImpl = delegate(ResultExecutedContext filterContext) { actions.Add("OnResultExecuted2"); } }; Action continuation = delegate { actions.Add("Continuation"); }; ActionResult actionResult = new ContinuationResult(continuation); ControllerBase controller = new Mock().Object; ControllerContext context = GetControllerContext(controller); ControllerActionInvokerHelper helper = new ControllerActionInvokerHelper(); List filters = new List() { filter1, filter2 }; // Act helper.PublicInvokeActionResultWithFilters(context, filters, actionResult); // Assert Assert.Equal(5, actions.Count); Assert.Equal("OnResultExecuting1", actions[0]); Assert.Equal("OnResultExecuting2", actions[1]); Assert.Equal("Continuation", actions[2]); Assert.Equal("OnResultExecuted2", actions[3]); Assert.Equal("OnResultExecuted1", actions[4]); } [Fact] public void InvokeResultFiltersPassesArgumentsCorrectly() { // Arrange bool wasCalled = false; Action continuation = delegate { Assert.True(false, "Continuation should not be called."); }; ActionResult actionResult = new ContinuationResult(continuation); ControllerBase controller = new Mock().Object; ControllerContext context = GetControllerContext(controller); ControllerActionInvokerHelper helper = new ControllerActionInvokerHelper(); ActionFilterImpl filter = new ActionFilterImpl() { OnResultExecutingImpl = delegate(ResultExecutingContext filterContext) { Assert.Same(actionResult, filterContext.Result); Assert.False(wasCalled); wasCalled = true; filterContext.Cancel = true; } }; List filters = new List() { filter }; // Act ResultExecutedContext result = helper.PublicInvokeActionResultWithFilters(context, filters, actionResult); // Assert Assert.True(wasCalled); Assert.Null(result.Exception); Assert.False(result.ExceptionHandled); Assert.Same(actionResult, result.Result); } [Fact] public void InvokeResultFilterWhereContinuationThrowsExceptionAndIsHandled() { // Arrange List actions = new List(); ActionResult actionResult = new EmptyResult(); Exception exception = new Exception(); ActionFilterImpl filter = new ActionFilterImpl() { OnResultExecutingImpl = delegate(ResultExecutingContext filterContext) { actions.Add("OnResultExecuting"); }, OnResultExecutedImpl = delegate(ResultExecutedContext filterContext) { actions.Add("OnResultExecuted"); Assert.Same(actionResult, filterContext.Result); Assert.Same(exception, filterContext.Exception); Assert.False(filterContext.ExceptionHandled); filterContext.ExceptionHandled = true; } }; Func continuation = delegate { actions.Add("Continuation"); throw exception; }; Mock mockResultExecutingContext = new Mock() { DefaultValue = DefaultValue.Mock }; mockResultExecutingContext.Setup(c => c.Result).Returns(actionResult); // Act ResultExecutedContext result = ControllerActionInvoker.InvokeActionResultFilter(filter, mockResultExecutingContext.Object, continuation); // Assert Assert.Equal(3, actions.Count); Assert.Equal("OnResultExecuting", actions[0]); Assert.Equal("Continuation", actions[1]); Assert.Equal("OnResultExecuted", actions[2]); Assert.Same(exception, result.Exception); Assert.True(result.ExceptionHandled); Assert.Same(actionResult, result.Result); } [Fact] public void InvokeResultFilterWhereContinuationThrowsExceptionAndIsNotHandled() { // Arrange List actions = new List(); ActionFilterImpl filter = new ActionFilterImpl() { OnResultExecutingImpl = delegate(ResultExecutingContext filterContext) { actions.Add("OnResultExecuting"); }, OnResultExecutedImpl = delegate(ResultExecutedContext filterContext) { actions.Add("OnResultExecuted"); } }; Func continuation = delegate { actions.Add("Continuation"); throw new Exception("Some exception message."); }; // Act & Assert Assert.Throws( delegate { ControllerActionInvoker.InvokeActionResultFilter(filter, new Mock() { DefaultValue = DefaultValue.Mock }.Object, continuation); }, "Some exception message."); Assert.Equal(3, actions.Count); Assert.Equal("OnResultExecuting", actions[0]); Assert.Equal("Continuation", actions[1]); Assert.Equal("OnResultExecuted", actions[2]); } [Fact] public void InvokeResultFilterWhereContinuationThrowsThreadAbortException() { // Arrange List actions = new List(); ActionResult actionResult = new EmptyResult(); Mock mockPreContext = new Mock() { DefaultValue = DefaultValue.Mock }; mockPreContext.Setup(c => c.Result).Returns(actionResult); ActionFilterImpl filter = new ActionFilterImpl() { OnResultExecutingImpl = delegate(ResultExecutingContext filterContext) { actions.Add("OnResultExecuting"); }, OnResultExecutedImpl = delegate(ResultExecutedContext filterContext) { Thread.ResetAbort(); actions.Add("OnResultExecuted"); Assert.Same(actionResult, filterContext.Result); Assert.Null(filterContext.Exception); Assert.False(filterContext.ExceptionHandled); } }; Func continuation = delegate { actions.Add("Continuation"); Thread.CurrentThread.Abort(); return null; }; // Act & Assert Assert.Throws( delegate { ControllerActionInvoker.InvokeActionResultFilter(filter, mockPreContext.Object, continuation); }, "Thread was being aborted."); Assert.Equal(3, actions.Count); Assert.Equal("OnResultExecuting", actions[0]); Assert.Equal("Continuation", actions[1]); Assert.Equal("OnResultExecuted", actions[2]); } [Fact] public void InvokeResultFilterWhereOnResultExecutingCancels() { // Arrange bool wasCalled = false; MethodInfo mi = typeof(object).GetMethod("ToString"); object[] paramValues = new object[0]; ActionResult actionResult = new EmptyResult(); ActionFilterImpl filter = new ActionFilterImpl() { OnResultExecutingImpl = delegate(ResultExecutingContext filterContext) { Assert.False(wasCalled); wasCalled = true; filterContext.Cancel = true; }, }; Func continuation = delegate { Assert.True(false, "The continuation should not be called."); return null; }; Mock mockResultExecutingContext = new Mock() { DefaultValue = DefaultValue.Mock }; mockResultExecutingContext.Setup(c => c.Result).Returns(actionResult); // Act ResultExecutedContext result = ControllerActionInvoker.InvokeActionResultFilter(filter, mockResultExecutingContext.Object, continuation); // Assert Assert.True(wasCalled); Assert.Null(result.Exception); Assert.True(result.Canceled); Assert.Same(actionResult, result.Result); } [Fact] public void InvokeResultFilterWithNormalControlFlow() { // Arrange List actions = new List(); ActionResult actionResult = new EmptyResult(); Mock mockPostContext = new Mock(); mockPostContext.Setup(c => c.Result).Returns(actionResult); ActionFilterImpl filter = new ActionFilterImpl() { OnResultExecutingImpl = delegate(ResultExecutingContext filterContext) { Assert.Same(actionResult, filterContext.Result); Assert.False(filterContext.Cancel); actions.Add("OnResultExecuting"); }, OnResultExecutedImpl = delegate(ResultExecutedContext filterContext) { Assert.Equal(mockPostContext.Object, filterContext); actions.Add("OnResultExecuted"); } }; Func continuation = delegate { actions.Add("Continuation"); return mockPostContext.Object; }; Mock mockResultExecutingContext = new Mock(); mockResultExecutingContext.Setup(c => c.Result).Returns(actionResult); // Act ResultExecutedContext result = ControllerActionInvoker.InvokeActionResultFilter(filter, mockResultExecutingContext.Object, continuation); // Assert Assert.Equal(3, actions.Count); Assert.Equal("OnResultExecuting", actions[0]); Assert.Equal("Continuation", actions[1]); Assert.Equal("OnResultExecuted", actions[2]); Assert.Same(result, mockPostContext.Object); } [Fact] public void InvokeMethodCallsOverriddenCreateActionResult() { // Arrange CustomResultInvokerController controller = new CustomResultInvokerController(); ControllerContext context = GetControllerContext(controller); CustomResultInvoker helper = new CustomResultInvoker(); MethodInfo mi = typeof(CustomResultInvokerController).GetMethod("ReturnCustomResult"); ReflectedActionDescriptor ad = new ReflectedActionDescriptor(mi, "ReturnCustomResult", new Mock().Object); IDictionary parameters = new Dictionary(); // Act ActionResult actionResult = helper.PublicInvokeActionMethod(context, ad, parameters); // Assert (arg got passed to method + back correctly) CustomResult customResult = Assert.IsType(actionResult); Assert.Equal("abc123", customResult.ReturnValue); } private static ControllerContext GetControllerContext(ControllerBase controller) { return GetControllerContext(controller, null); } private static ControllerContext GetControllerContext(ControllerBase controller, IDictionary values, Action validateInputCallback = null) { SimpleValueProvider valueProvider = new SimpleValueProvider(); controller.ValueProvider = valueProvider; if (values != null) { foreach (var entry in values) { valueProvider[entry.Key] = entry.Value; } } Mock mockControllerContext = new Mock() { DefaultValue = DefaultValue.Mock }; mockControllerContext.Setup(c => c.HttpContext.Request.ValidateInput()).Callback(() => { if (!controller.ValidateRequest) { Assert.True(false, "ValidateRequest() should not be called if the controller opted out."); } if (validateInputCallback != null) { // signal to caller that ValidateInput was called validateInputCallback(); } }); mockControllerContext.Setup(c => c.HttpContext.Session).Returns((HttpSessionStateBase)null); mockControllerContext.Setup(c => c.Controller).Returns(controller); return mockControllerContext.Object; } private class EmptyActionFilterAttribute : ActionFilterAttribute { } private abstract class KeyedFilterAttribute : FilterAttribute { public string Key { get; set; } } private class KeyedAuthorizationFilterAttribute : KeyedFilterAttribute, IAuthorizationFilter { public void OnAuthorization(AuthorizationContext filterContext) { throw new NotImplementedException(); } } [AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, Inherited = true, AllowMultiple = true)] private class KeyedActionFilterAttribute : KeyedFilterAttribute, IActionFilter, IResultFilter { public void OnActionExecuting(ActionExecutingContext filterContext) { throw new NotImplementedException(); } public void OnActionExecuted(ActionExecutedContext filterContext) { throw new NotImplementedException(); } public void OnResultExecuting(ResultExecutingContext filterContext) { throw new NotImplementedException(); } public void OnResultExecuted(ResultExecutedContext filterContext) { throw new NotImplementedException(); } } private class ActionFilterImpl : IActionFilter, IResultFilter { public Action OnActionExecutingImpl { get; set; } public void OnActionExecuting(ActionExecutingContext filterContext) { OnActionExecutingImpl(filterContext); } public Action OnActionExecutedImpl { get; set; } public void OnActionExecuted(ActionExecutedContext filterContext) { OnActionExecutedImpl(filterContext); } public Action OnResultExecutingImpl { get; set; } public void OnResultExecuting(ResultExecutingContext filterContext) { OnResultExecutingImpl(filterContext); } public Action OnResultExecutedImpl { get; set; } public void OnResultExecuted(ResultExecutedContext filterContext) { OnResultExecutedImpl(filterContext); } } [KeyedActionFilter(Key = "BaseClass", Order = 0)] [KeyedAuthorizationFilter(Key = "BaseClass", Order = 0)] private class GetMemberChainController : Controller { [KeyedActionFilter(Key = "BaseMethod", Order = 0)] [KeyedAuthorizationFilter(Key = "BaseMethod", Order = 0)] public virtual void SomeVirtual() { } } [KeyedActionFilter(Key = "DerivedClass", Order = 1)] private class GetMemberChainDerivedController : GetMemberChainController { } [KeyedActionFilter(Key = "SubderivedClass", Order = 2)] private class GetMemberChainSubderivedController : GetMemberChainDerivedController { [KeyedActionFilter(Key = "SubderivedMethod", Order = 2)] public override void SomeVirtual() { } } // This controller serves only to test vanilla method invocation - nothing exciting here private class BasicMethodInvokeController : Controller { public ActionResult ReturnsRenderView(object viewItem) { return View("ReturnsRenderView", viewItem); } } private class BlankController : Controller { } private sealed class CustomResult : ActionResult { public object ReturnValue { get; set; } public override void ExecuteResult(ControllerContext context) { throw new NotImplementedException(); } } private sealed class CustomResultInvokerController : Controller { public object ReturnCustomResult() { return "abc123"; } } private sealed class CustomResultInvoker : ControllerActionInvokerHelper { protected override ActionResult CreateActionResult(ControllerContext controllerContext, ActionDescriptor actionDescriptor, object actionReturnValue) { return new CustomResult { ReturnValue = actionReturnValue }; } } private class ContinuationController : Controller { private Func _continuation; public ContinuationController(Func continuation) { _continuation = continuation; } public ActionResult Go() { return _continuation(); } public static MethodInfo GoMethod { get { return typeof(ContinuationController).GetMethod("Go"); } } } private class ContinuationResult : ActionResult { private Action _continuation; public ContinuationResult(Action continuation) { _continuation = continuation; } public override void ExecuteResult(ControllerContext context) { _continuation(); } } private class EmptyController : Controller { } // This controller serves to test the default action method matching mechanism private class FindMethodController : Controller { public ActionResult ValidActionMethod() { return null; } [NonAction] public virtual ActionResult NonActionMethod() { return null; } [NonAction] public ActionResult DerivedIsActionMethod() { return null; } public ActionResult MethodOverloaded() { return null; } public ActionResult MethodOverloaded(string s) { return null; } public void WrongReturnType() { } protected ActionResult ProtectedMethod() { return null; } private ActionResult PrivateMethod() { return null; } internal ActionResult InternalMethod() { return null; } public override string ToString() { // originally defined on Object return base.ToString(); } public ActionResult Property { get { return null; } } #pragma warning disable 0067 // CS0067: Event declared but never used. We use reflection to access this member. public event EventHandler Event; #pragma warning restore 0067 } private class DerivedFindMethodController : FindMethodController { public override ActionResult NonActionMethod() { return base.NonActionMethod(); } // FindActionMethod() should accept this as a valid method since [NonAction] doesn't appear // in its inheritance chain. public new ActionResult DerivedIsActionMethod() { return base.DerivedIsActionMethod(); } } // Similar to FindMethodController, but tests generics support specifically private class GenericFindMethodController : Controller { public ActionResult ClosedGenericMethod(T t) { return null; } public ActionResult OpenGenericMethod(U t) { return null; } } // Allows for testing parameter conversions, etc. private class ParameterTestingController : Controller { public ParameterTestingController() { Values = new Dictionary(); } public IDictionary Values { get; private set; } public void Foo(string foo, string bar, string baz) { Values["foo"] = foo; Values["bar"] = bar; Values["baz"] = baz; } public void HasOutParam(out string foo) { foo = null; } public void HasRefParam(ref string foo) { } public void Parameterless() { } public void TakesInt(int id) { Values["id"] = id; } public ActionResult TakesNullableInt(int? id) { Values["id"] = id; return null; } public void TakesString(string id) { } public void TakesDateTime(DateTime id) { } } // Provides access to the protected members of ControllerActionInvoker public class ControllerActionInvokerHelper : ControllerActionInvoker { public ControllerActionInvokerHelper() { // set instance caches to prevent modifying global test application state DescriptorCache = new ControllerDescriptorCache(); } public ControllerActionInvokerHelper(params object[] filters) : base(filters) { // set instance caches to prevent modifying global test application state DescriptorCache = new ControllerDescriptorCache(); } public virtual ActionResult PublicCreateActionResult(ControllerContext controllerContext, ActionDescriptor actionDescriptor, object actionReturnValue) { return base.CreateActionResult(controllerContext, actionDescriptor, actionReturnValue); } protected override ActionResult CreateActionResult(ControllerContext controllerContext, ActionDescriptor actionDescriptor, object actionReturnValue) { return PublicCreateActionResult(controllerContext, actionDescriptor, actionReturnValue); } public virtual ActionDescriptor PublicFindAction(ControllerContext controllerContext, ControllerDescriptor controllerDescriptor, string actionName) { return base.FindAction(controllerContext, controllerDescriptor, actionName); } protected override ActionDescriptor FindAction(ControllerContext controllerContext, ControllerDescriptor controllerDescriptor, string actionName) { return PublicFindAction(controllerContext, controllerDescriptor, actionName); } public virtual ControllerDescriptor PublicGetControllerDescriptor(ControllerContext controllerContext) { return base.GetControllerDescriptor(controllerContext); } protected override ControllerDescriptor GetControllerDescriptor(ControllerContext controllerContext) { return PublicGetControllerDescriptor(controllerContext); } public virtual FilterInfo PublicGetFilters(ControllerContext controllerContext, ActionDescriptor actionDescriptor) { return base.GetFilters(controllerContext, actionDescriptor); } protected override FilterInfo GetFilters(ControllerContext controllerContext, ActionDescriptor actionDescriptor) { return PublicGetFilters(controllerContext, actionDescriptor); } public virtual object PublicGetParameterValue(ControllerContext controllerContext, ParameterDescriptor parameterDescriptor) { return base.GetParameterValue(controllerContext, parameterDescriptor); } protected override object GetParameterValue(ControllerContext controllerContext, ParameterDescriptor parameterDescriptor) { return PublicGetParameterValue(controllerContext, parameterDescriptor); } public virtual IDictionary PublicGetParameterValues(ControllerContext controllerContext, ActionDescriptor actionDescriptor) { return base.GetParameterValues(controllerContext, actionDescriptor); } protected override IDictionary GetParameterValues(ControllerContext controllerContext, ActionDescriptor actionDescriptor) { return PublicGetParameterValues(controllerContext, actionDescriptor); } public virtual ActionResult PublicInvokeActionMethod(ControllerContext controllerContext, ActionDescriptor actionDescriptor, IDictionary parameters) { return base.InvokeActionMethod(controllerContext, actionDescriptor, parameters); } protected override ActionResult InvokeActionMethod(ControllerContext controllerContext, ActionDescriptor actionDescriptor, IDictionary parameters) { return PublicInvokeActionMethod(controllerContext, actionDescriptor, parameters); } public virtual ActionExecutedContext PublicInvokeActionMethodWithFilters(ControllerContext controllerContext, IList filters, ActionDescriptor actionDescriptor, IDictionary parameters) { return base.InvokeActionMethodWithFilters(controllerContext, filters, actionDescriptor, parameters); } protected override ActionExecutedContext InvokeActionMethodWithFilters(ControllerContext controllerContext, IList filters, ActionDescriptor actionDescriptor, IDictionary parameters) { return PublicInvokeActionMethodWithFilters(controllerContext, filters, actionDescriptor, parameters); } public virtual void PublicInvokeActionResult(ControllerContext controllerContext, ActionResult actionResult) { base.InvokeActionResult(controllerContext, actionResult); } protected override void InvokeActionResult(ControllerContext controllerContext, ActionResult actionResult) { PublicInvokeActionResult(controllerContext, actionResult); } public virtual ResultExecutedContext PublicInvokeActionResultWithFilters(ControllerContext controllerContext, IList filters, ActionResult actionResult) { return base.InvokeActionResultWithFilters(controllerContext, filters, actionResult); } protected override ResultExecutedContext InvokeActionResultWithFilters(ControllerContext controllerContext, IList filters, ActionResult actionResult) { return PublicInvokeActionResultWithFilters(controllerContext, filters, actionResult); } public virtual AuthorizationContext PublicInvokeAuthorizationFilters(ControllerContext controllerContext, IList filters, ActionDescriptor actionDescriptor) { return base.InvokeAuthorizationFilters(controllerContext, filters, actionDescriptor); } protected override AuthorizationContext InvokeAuthorizationFilters(ControllerContext controllerContext, IList filters, ActionDescriptor actionDescriptor) { return PublicInvokeAuthorizationFilters(controllerContext, filters, actionDescriptor); } public virtual ExceptionContext PublicInvokeExceptionFilters(ControllerContext controllerContext, IList filters, Exception exception) { return base.InvokeExceptionFilters(controllerContext, filters, exception); } protected override ExceptionContext InvokeExceptionFilters(ControllerContext controllerContext, IList filters, Exception exception) { return PublicInvokeExceptionFilters(controllerContext, filters, exception); } } public class AuthorizationFilterHelper : IAuthorizationFilter { private IList _callQueue; public ActionResult ShortCircuitResult; public AuthorizationFilterHelper(IList callQueue) { _callQueue = callQueue; } public void OnAuthorization(AuthorizationContext filterContext) { _callQueue.Add(this); if (ShortCircuitResult != null) { filterContext.Result = ShortCircuitResult; } } } public class ExceptionFilterHelper : IExceptionFilter { private IList _callQueue; public bool ShouldHandleException; public ExceptionContext ContextPassed; public ExceptionFilterHelper(IList callQueue) { _callQueue = callQueue; } public void OnException(ExceptionContext filterContext) { _callQueue.Add(this); if (ShouldHandleException) { filterContext.ExceptionHandled = true; } ContextPassed = filterContext; } } private class CustomConverterController : Controller { public void ParameterWithoutBindAttribute([PredicateReflector] string someParam) { } public void ParameterHasBindAttribute([Bind(Include = "foo"), PredicateReflector] string someParam) { } public void ParameterHasDefaultValueAttribute([DefaultValue(42)] int foo) { } public void ParameterHasFieldPrefix([Bind(Prefix = "bar")] string foo) { } public void ParameterHasNullFieldPrefix([Bind(Include = "whatever")] string foo) { } public void ParameterHasEmptyFieldPrefix([Bind(Prefix = "")] MySimpleModel foo) { } public void ParameterHasNoPrefixAndComplexType(MySimpleModel foo) { } public void ParameterHasPrefixAndComplexType([Bind(Prefix = "badprefix")] MySimpleModel foo) { } public void ParameterHasNoConverters(string foo) { } public void ParameterHasOneConverter([MyCustomConverter] string foo) { } public void ParameterHasTwoConverters([MyCustomConverter, MyCustomConverter] string foo) { } } public class MySimpleModel { public int IntProp { get; set; } public string StringProp { get; set; } } [AttributeUsage(AttributeTargets.Parameter, AllowMultiple = true, Inherited = false)] private class PredicateReflectorAttribute : CustomModelBinderAttribute { public override IModelBinder GetBinder() { return new MyConverter(); } private class MyConverter : IModelBinder { public object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext) { string s = String.Format("foo={0}&bar={1}", bindingContext.PropertyFilter("foo"), bindingContext.PropertyFilter("bar")); return s; } } } [AttributeUsage(AttributeTargets.Parameter, AllowMultiple = true, Inherited = false)] private class MyCustomConverterAttribute : CustomModelBinderAttribute { public override IModelBinder GetBinder() { return new MyConverter(); } private class MyConverter : IModelBinder { public object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext) { string s = bindingContext.ModelName + "_" + bindingContext.ModelType.Name; return s; } } } // helper class for making sure that we're performing culture-invariant string conversions public class CultureReflector : IFormattable { string IFormattable.ToString(string format, IFormatProvider formatProvider) { CultureInfo cInfo = (CultureInfo)formatProvider; return cInfo.ThreeLetterISOLanguageName; } } } }