2010-05-19 2 views
11

У меня есть приложение 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, есть ли что-то более точное с определением перегрузки, которая вызывается?

ответ

2

Хорошо, это решение, с которым я столкнулся.

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 не в состоянии определить метод просил, я принести коллекцию параметров из коллекции ActionExecutingContext.ActionParameters и построить массив типов параметров, передаваемых в запросе. Затем я могу использовать перегрузку Type.GetMethod (string, type()), чтобы более подробно описать мой запрос.

0

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

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

+0

Сайт является порталом для наших клиентов. У меня есть некоторые страницы, такие как домашняя страница с частичными представлениями от других контроллеров. Я хочу вернуть частичный вид с сообщением в обратном направлении к родительскому представлению. Домашний контроллер всегда будет доступен, но контроллер отчетов может и не быть. Виджет отчетов должен просто показать вежливое сообщение. – Nick

+0

@Nick: тогда почему бы просто не сделать что-то вроде filterContext.Result = New PartialViewResult (...), независимо от фактического результата действия, возвращаемого методом действия? –

+0

Это нормально, если они ожидают частичный вид. Если они попадут/Отчеты/Индекс, хотя им не понравится возвращение частичного просмотра. Я обновил свой вопрос с прогрессом, который я сделал с использованием рефлексии. – Nick

9

Я создал 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; 
     } 
    } 
+1

Интересное решение, спасибо. Обратите внимание, что если filterContext.ActionDescriptor имеет тип System.Web.Mvc.ReflectedActionDescriptor, он уже будет иметь свойство MethodInfo, поэтому вам не нужно будет затруднять его определение. –

Смежные вопросы