RequestContext - RouteData does not contain action

2020-04-16 01:54发布

So i've created my own ControllerFactory and i'm overloading GetControllerSessionBehavior in order to extend the MVC behavior.

To do my custom work i have to use reflection on the called action. However i've stumbled upon a weird issue - i can't retrieve the action by accessing RequestContext.RouteData

While setting up a reproduction sample for this i was not able to reproduce the error.

Is anyone aware of possible reasons for this or knows how to retrieve the action by calling a method with the request context other than this?

public class CustomControllerFactory : DefaultControllerFactory
{
    protected override SessionStateBehavior GetControllerSessionBehavior(RequestContext requestContext, Type controllerType)
    {
        if (!requestContext.RouteData.Values.ContainsKey("action"))
            return base.GetControllerSessionBehavior(requestContext, controllerType);

        var controllerAction = requestContext.RouteData.Values["action"];
        var action = controllerAction.ToString();

        var actionMethod = controllerType.GetMember(action, MemberTypes.Method, BindingFlags.Instance | BindingFlags.Public).FirstOrDefault();
        if(actionMethod == null)
            return base.GetControllerSessionBehavior(requestContext, controllerType);

        var cattr = actionMethod.GetCustomAttribute<SessionStateActionAttribute>();
        if (cattr != null)
            return cattr.Behavior;

        return base.GetControllerSessionBehavior(requestContext, controllerType);
    }
}

Action which i can call just fine but can't access the action name of within my controller factory:

    [Route("Open/{createModel:bool?}/{tlpId:int}/{siteId:int?}")]
    public ActionResult Open(int tlpId, int? siteId, bool? createModel = true)
    {
    }

Any ideas welcome.

Update:

The problem seems to be related to attribute routing. While it's working fine in repro it doesn't work in production for me.

Found this along the way - Once this is answered i'll have my proper solution too i guess.

Update 2:

Interesting. Reproduction MVC Version 5.0.0.0, Production 5.2.2. Possible introduction of bug?

1条回答
地球回转人心会变
2楼-- · 2020-04-16 02:25

I can confirm that there was a breaking change to attribute routing between 5.0.0 and 5.1.1. I reported the issue here. However, for my use case Microsoft was able to provide an acceptable workaround.

On the other hand, the problem you are bumping into looks like another culprit. For attribute routing, the route values are stored in a nested route key named MS_DirectRouteMatches. I am not sure exactly which version that changed in, but I know it happened v5+.

So, to fix your issue, you will need to check for the existence of a nested RouteData collection, and use instead of the normal RouteData in the case it exists.

var routeData = requestContext.RouteData;
if (routeData.Values.ContainsKey("MS_DirectRouteMatches"))
{
    routeData = ((IEnumerable<RouteData>)routeData.Values["MS_DirectRouteMatches"]).First();
}
var controllerAction = routeData.Values["action"];
var action = controllerAction.ToString();

BTW - In the linked question you provided, the asker assumed that there is a possibility where a request can match more than one route. But that is not possible - a request will match 0 or 1 route, but never more than one.

查看更多
登录 后发表回答