Могу ли я получить тип возвращаемого действия из фильтра действий?

У меня есть приложение ASP.NET MVC 2, в котором я создаю настраиваемый фильтр действий. Этот фильтр находится на контроллерах в приложении и проверяет из базы данных, доступна ли эта функция в настоящее время.

Public Overrides Sub OnActionExecuting(ByVal filterContext As System.Web.Mvc.ActionExecutingContext)
  Try
    ' Check controller name against database.
    Dim controllerName = filterContext.Controller.GetType().Name
    controllerName = controllerName.Remove(controllerName.Length - 10)
    ' Look up availability.
    Dim available As Boolean = _coreService.GetControllerAvailability(controllerName)
    If Not available Then
      ' Redirect to unavailable notice.
      filterContext.Result = New RedirectResult("/Home/Unavailable/")
    End If
  Catch ex As Exception
    _eventLogger.LogWarning(ex, EventLogEntryType.Error)
    Throw
  End Try
End Sub

Моя проблема в том, что в зависимости от запрошенного действия мне нужно перенаправить пользователя на действие, которое возвращает либо представление, частичные представления, либо JSON.

Учитывая ActionExecutingContext, могу ли я узнать, каков тип возврата первоначально запрошенного действия?

EDIT:

Хорошо, я приближаюсь, но имею еще одну проблему.

Public Overrides Sub OnActionExecuting(ByVal filterContext As System.Web.Mvc.ActionExecutingContext)
  Try
    ' Check controller name against database.
    Dim controllerName = filterContext.Controller.GetType().Name
    Dim shortName = controllerName.Remove(controllerName.Length - 10)
    ' Look up availability.
    Dim available As Boolean = _coreService.GetControllerAvailability(shortName)
    If Not available Then
      ' find out what type is expected to be returned
      Dim actionName As String = filterContext.ActionDescriptor.ActionName
      Dim controllerType = Type.GetType("Attenda.Stargate.Web." & controllerName)
      Dim actionMethodInfo = controllerType.GetMethod(actionName)
      Dim actionReturnType = actionMethodInfo.ReturnType.Name

      Select Case actionReturnType
        Case "PartialViewResult"
          filterContext.Result = New RedirectResult("/Home/UnavailablePartial/")
        Case "JsonResult"
          filterContext.Result = New RedirectResult("/Home/UnavailableJson/")
        Case Else
          filterContext.Result = New RedirectResult("/Home/Unavailable/")
      End Select

    End If
  Catch ex As Exception
    _eventLogger.LogWarning(ex, EventLogEntryType.Error)
    Throw
  End Try
End Sub

Я могу использовать отражение, чтобы найти возвращаемый тип метода действия. Моя проблема в том, что на контроллере есть следующие методы:

Public Function Create() As ViewResult
  Return View()
End Function

<AcceptVerbs(HttpVerbs.Post)>
Public Function Create(values as FormCollection) As ViewResult
  ' Do stuff here
End Function

Я получаю исключение AmbiguousMatchException.

С информацией, которая у меня есть в методе OnActionExecuting, есть ли что-то более точное с определением перегрузки, которая вызывается?

Ответ 1

Хорошо, это решение, которое я придумал.

Public Overrides Sub OnActionExecuting(ByVal filterContext As System.Web.Mvc.ActionExecutingContext)
  Try
    ' Check controller name against database.
    Dim controllerName = filterContext.Controller.GetType().Name
    Dim shortName = controllerName.Remove(controllerName.Length - 10)
    ' Look up availability.
    Dim available As Boolean = _coreService.GetControllerAvailability(shortName)
    If Not available Then
      ' find out what type is expected to be returned
      Dim actionName As String = filterContext.ActionDescriptor.ActionName
      Dim controllerType = Type.GetType("Attenda.Stargate.Web." & controllerName)
      Dim actionMethodInfo As MethodInfo
      Try
        actionMethodInfo = controllerType.GetMethod(actionName)
      Catch ex As AmbiguousMatchException
        ' Try to find a match using the parameters passed through
        Dim actionParams = filterContext.ActionParameters
        Dim paramTypes As New List(Of Type)
        For Each p In actionParams
          paramTypes.Add(p.Value.GetType())
        Next
        actionMethodInfo = controllerType.GetMethod(actionName, paramTypes.ToArray)
      End Try
      Dim actionReturnType = actionMethodInfo.ReturnType.Name

      Select Case actionReturnType
        Case "PartialViewResult"
          filterContext.Result = New RedirectResult("/Home/UnavailablePartial/")
        Case "JsonResult"
          filterContext.Result = New RedirectResult("/Home/UnavailableJson/")
        Case Else
          filterContext.Result = New RedirectResult("/Home/Unavailable/")
      End Select

    End If
  Catch ex As Exception
    _eventLogger.LogWarning(ex, EventLogEntryType.Error)
    Throw
  End Try
End Sub

Если вызов Type.GetMethod(string) не может идентифицировать запрошенный метод, я беру коллекцию параметров из коллекции ActionExecutingContext.ActionParameters и создаю массив типов параметров, переданных в запросе. Затем я могу использовать перегрузку Type.GetMethod(string, type()) для более конкретного запроса.

Ответ 2

Я создал на этом основе атрибут AuthenticationFilterAttribute, который возвращает разные результаты на основе типа:

    /// <summary>
    /// Access to the action will be blocked if the user is not logged in. 
    ///  Apply this to the controller level or individual actions as an attribute.
    /// </summary>
    public class AuthenticationFilterAttribute : ActionFilterAttribute
    {
        protected const string InvalidAccess = "Invalid access";

        public override void OnActionExecuting(ActionExecutingContext filterContext)
        {
            // Find out if the user is logged in: 
            Controller controller = (Controller)filterContext.Controller;
            if (!controller.User.Identity.IsAuthenticated)
            {
                switch (GetExpectedReturnType(filterContext).Name)
                {
                    case "JsonResult":
                        var jsonResult = new JsonResult();
                        jsonResult.Data = new { Error = true, ErrorMessage = InvalidAccess };
                        jsonResult.JsonRequestBehavior = JsonRequestBehavior.AllowGet;
                        filterContext.Result = jsonResult;
                        break;

                    // Assume same behaviour as ActionResult
                    default: 
                        var actionResult = new ContentResult();
                        actionResult.Content = InvalidAccess;
                        filterContext.Result = actionResult;
                        break;
                }
            }
        }

        private Type GetExpectedReturnType(ActionExecutingContext filterContext)
        {
            // Find out what type is expected to be returned
            string actionName = filterContext.ActionDescriptor.ActionName;
            Type controllerType = filterContext.Controller.GetType();
            MethodInfo actionMethodInfo = default(MethodInfo);
            try
            {
                actionMethodInfo = controllerType.GetMethod(actionName);
            }
            catch (AmbiguousMatchException ex)
            {
                // Try to find a match using the parameters passed through
                var actionParams = filterContext.ActionParameters;
                List<Type> paramTypes = new List<Type>();
                foreach (var p in actionParams)
                {
                    paramTypes.Add(p.Value.GetType());
                }

                actionMethodInfo = controllerType.GetMethod(actionName, paramTypes.ToArray());
            }

            return actionMethodInfo.ReturnType;
        }
    }

Ответ 3

К моменту вызова OnActionExecuting метод действия еще не выполнен, поэтому вы не знаете, будет ли этот метод действий возвращать этот подкласс ActionResult. Итак, если вы не можете пойти с реализацией CIL-анализа (который, я думаю, может стать уродливым очень быстро), я не думаю, что вы хотите сделать.

Это не факт, что вы перенаправляете пользователей на представление, когда контроллер недостаточно доступен? Я имею в виду, я не понимаю, почему вы хотите перенаправить пользователей на результат JSON или частичный вид.