Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Intercept action controller with arguments in mvc 5 throw exception #419

Closed
zu1779 opened this issue May 20, 2018 · 7 comments
Closed

Intercept action controller with arguments in mvc 5 throw exception #419

zu1779 opened this issue May 20, 2018 · 7 comments

Comments

@zu1779
Copy link

zu1779 commented May 20, 2018

I'm not able to intercept a controller action with arguments.
I'm using Asp.Net MVC 5.2.6 with LightInject 5.1.3 and LightInject.Interception 2.0.0.
I receive the following error:

Server Error in '/' Application.
Unable to cast object of type 'System.Object[]' to type 'System.Web.Mvc.CustomModelBinderAttribute[]'.
Description: An unhandled exception occurred during the execution of the current web request. Please review the stack trace for more information about the error and where it originated in the code. 

Exception Details: System.InvalidCastException: Unable to cast object of type 'System.Object[]' to type 'System.Web.Mvc.CustomModelBinderAttribute[]'.

Source Error: 

An unhandled exception was generated during the execution of the current web request. Information regarding the origin and location of the exception can be identified using the exception stack trace below.

Stack Trace: 


[InvalidCastException: Unable to cast object of type 'System.Object[]' to type 'System.Web.Mvc.CustomModelBinderAttribute[]'.]
   System.Web.Mvc.ModelBinders.GetBinderFromAttributes(ICustomAttributeProvider element, Action`1 errorAction) +42
   System.Web.Mvc.ReflectedParameterBindingInfo.get_Binder() +102
   System.Web.Mvc.ControllerActionInvoker.GetModelBinder(ParameterDescriptor parameterDescriptor) +28
   System.Web.Mvc.ControllerActionInvoker.GetParameterValue(ControllerContext controllerContext, ParameterDescriptor parameterDescriptor) +38
   System.Web.Mvc.ControllerActionInvoker.GetParameterValues(ControllerContext controllerContext, ActionDescriptor actionDescriptor) +105
   System.Web.Mvc.Async.<>c__DisplayClass3_1.<BeginInvokeAction>b__0(AsyncCallback asyncCallback, Object asyncState) +640
   System.Web.Mvc.Async.WrappedAsyncResult`1.CallBeginDelegate(AsyncCallback callback, Object callbackState) +14
   System.Web.Mvc.Async.WrappedAsyncResultBase`1.Begin(AsyncCallback callback, Object state, Int32 timeout) +128
   System.Web.Mvc.Async.AsyncControllerActionInvoker.BeginInvokeAction(ControllerContext controllerContext, String actionName, AsyncCallback callback, Object state) +346
   System.Web.Mvc.<>c.<BeginExecuteCore>b__152_0(AsyncCallback asyncCallback, Object asyncState, ExecuteCoreState innerState) +27
   System.Web.Mvc.Async.WrappedAsyncVoid`1.CallBeginDelegate(AsyncCallback callback, Object callbackState) +30
   System.Web.Mvc.Async.WrappedAsyncResultBase`1.Begin(AsyncCallback callback, Object state, Int32 timeout) +128
   System.Web.Mvc.Controller.BeginExecuteCore(AsyncCallback callback, Object state) +494
   System.Web.Mvc.<>c.<BeginExecute>b__151_1(AsyncCallback asyncCallback, Object callbackState, Controller controller) +16
   System.Web.Mvc.Async.WrappedAsyncVoid`1.CallBeginDelegate(AsyncCallback callback, Object callbackState) +20
   System.Web.Mvc.Async.WrappedAsyncResultBase`1.Begin(AsyncCallback callback, Object state, Int32 timeout) +128
   System.Web.Mvc.Controller.BeginExecute(RequestContext requestContext, AsyncCallback callback, Object state) +403
   System.Web.Mvc.Controller.System.Web.Mvc.Async.IAsyncController.BeginExecute(RequestContext requestContext, AsyncCallback callback, Object state) +16
   System.Web.Mvc.<>c.<BeginProcessRequest>b__20_0(AsyncCallback asyncCallback, Object asyncState, ProcessRequestState innerState) +54
   System.Web.Mvc.Async.WrappedAsyncVoid`1.CallBeginDelegate(AsyncCallback callback, Object callbackState) +30
   System.Web.Mvc.Async.WrappedAsyncResultBase`1.Begin(AsyncCallback callback, Object state, Int32 timeout) +128
   System.Web.Mvc.MvcHandler.BeginProcessRequest(HttpContextBase httpContext, AsyncCallback callback, Object state) +427
   System.Web.Mvc.MvcHandler.BeginProcessRequest(HttpContext httpContext, AsyncCallback callback, Object state) +48
   System.Web.Mvc.MvcHandler.System.Web.IHttpAsyncHandler.BeginProcessRequest(HttpContext context, AsyncCallback cb, Object extraData) +16
   System.Web.CallHandlerExecutionStep.System.Web.HttpApplication.IExecutionStep.Execute() +103
   System.Web.HttpApplication.ExecuteStepImpl(IExecutionStep step) +48
   System.Web.HttpApplication.ExecuteStep(IExecutionStep step, Boolean& completedSynchronously) +159

My code is (I do not insert views because are meaningless):

// Global.asax
namespace TestMVCInjection
{
	using System;
	using System.Diagnostics;
	using System.Linq;
	using System.Web;
	using System.Web.Mvc;
	using System.Web.Routing;
	using LightInject;
	using LightInject.Interception;

	using TestMVCInjection.Controllers;

	public class MvcApplication : HttpApplication
	{
		public void Application_Start()
		{
			AreaRegistration.RegisterAllAreas();
			//FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
			GlobalFilters.Filters.Add(new HandleErrorAttribute());
			//RouteConfig.RegisterRoutes(RouteTable.Routes);
			RouteTable.Routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
			RouteTable.Routes.MapRoute(
				name: "Default",
				url: "{controller}/{action}/{id}",
				defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional }
			);

			RegisterLightInject();
		}

		private void RegisterLightInject()
		{
			var container = new ServiceContainer();
			container.RegisterControllers();
			container.EnableMvc();
			container.Intercept((ServiceRegistration c) => c.ServiceType == typeof(HomeController), (IServiceFactory c) => (IInterceptor)new DebugInterceptor());
		}
	}

	public class DebugInterceptor : IInterceptor
	{
		public object Invoke(IInvocationInfo invocationInfo)
		{
			object returnValue = null;
			// Verifico se devo intercettare il metodo
			var mi = invocationInfo.Method;
			var sw = Stopwatch.StartNew();
			string timestamp = DateTime.Now.ToString("yyyy-MM-ddTHH:mm:ss.fff \"GMT\"zzz");
			string method = $"{invocationInfo.Method.DeclaringType.Name}.{invocationInfo.Method.Name}";
			try
			{
				var parNames = invocationInfo.Method.GetParameters().Select(d => $"{d.Name}: {d.ParameterType}").ToList();
				var parValues = invocationInfo.Arguments.Select(c => $"{c}").ToList();
				string parameters = string.Join(Environment.NewLine, parNames.Select((n, i) => $"{parNames[i]} = {parValues[i]}"));
				string message = $"{nameof(timestamp).ToUpper()}={timestamp}{Environment.NewLine}{nameof(method).ToUpper()}={method}{Environment.NewLine}{nameof(parameters).ToUpper()}{Environment.NewLine}{parameters}";
				Debug.WriteLine(message);
				returnValue = invocationInfo.Proceed();
				message = $"TOOK {nameof(sw.ElapsedMilliseconds)} = {sw.ElapsedMilliseconds} for returning {returnValue} for method {method}";
				Debug.WriteLine(message);
				return returnValue;
			}
			catch (Exception)
			{
				string message = $"Took {nameof(sw.ElapsedMilliseconds)} = {sw.ElapsedMilliseconds} for throwing an exception method {method}";
				Debug.WriteLine(message);
				throw;
			}
		}
	}
}

My Controller is:

namespace TestMVCInjection.Controllers
{
	using System.Diagnostics;
	using System.Web.Mvc;

	public class HomeController : Controller
	{
		public virtual ActionResult Index(string id)
		{
			Debug.WriteLine("-----> INDEX START <-----");
			var response = View((object)id);
			Debug.WriteLine("-----> INDEX END <-----");
			return response;
		}
	}
}
@seesharper
Copy link
Owner

@seesharper
Copy link
Owner

Seems related to custom attributes

@seesharper
Copy link
Owner

Progress so far is that I tested with an inherited controller to see if that was a problem

The generated proxy type is also just a subclass of the controller.

Using the following HomeController subclass works as expected.

public class InheritedController : HomeController
    public class InheritedController : HomeController
    {
        public override ActionResult Index(string id)
        {
            return base.Index(id);
        }
    }

This however does not work

var container = new ServiceContainer();
var proxyType = CreateProxyType();
container.Register(typeof(HomeController), proxyType, new PerRequestLifeTime());

CreateProxyType looks like this

private Type CreateProxyType()
{
    ProxyBuilder pb = new ProxyBuilder();
    ProxyDefinition proxyDefinition = new ProxyDefinition(typeof(HomeController));
    proxyDefinition.Implement(() => new DebugInterceptor(), m => m.Name == "Index");
    var proxyType = pb.GetProxyType(proxyDefinition);
    return proxyType;
}

Next step is to see how the generated proxy type and the InheritedController are different.

@seesharper
Copy link
Owner

When overriding a method we don't set the parameter names. That could be a problem with regards to model binding.

.method public hidebysig virtual 
	instance class [System.Web.Mvc]System.Web.Mvc.ActionResult Index (
		string text
	) cil managed 

Notice how the parameter name is missing in the generated proxy

.method public hidebysig virtual 
	instance class [System.Web.Mvc]System.Web.Mvc.ActionResult Index (
		string ''
	) cil managed

@seesharper
Copy link
Owner

seesharper commented May 28, 2018

That's it. Adding the parameter name to the proxy method seems to fix the issue

@seesharper
Copy link
Owner

@seesharper
Copy link
Owner

@zu1779 LightInject.Interception 2.0.1 is now available from the official NuGet feed

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants