2010-05-19 84 views
11

我有一个ASP.NET MVC 2应用程序,我在其中创建自定义操作筛选器。此过滤器位于应用程序中的控制器上,并从数据库中验证该功能当前是否可用。我可以从Action Filter获取Action的返回类型吗?

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我可以找出最初请求的动作的返回类型是什么?编辑:

好吧,我越来越近,但有另一个问题。

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时,action方法尚未执行,因此无法知道该操作方法是否要返回ActionResult的哪个子类。所以,除非你能够使用CIL分析实现(我认为它可能会很快变得丑陋),否则我不认为你想要做什么是可能的。

这就是说,当控制器不够用时,您是不是将用户重定向到视图?我的意思是,我不明白你为什么要将用户重定向到JSON结果或部分视图。

+0

该网站是为我们的客户门户。我有一些页面,如其他控制器的部分视图的主页。我想返回一个带有消息的部分视图回到父视图。家庭控制器将始终可用,但报告控制器可能不会。报告小部件应该只显示礼貌的消息。 – Nick 2010-05-19 12:39:35

+0

@Nick:那么为什么不只是像filterContext.Result = New PartialViewResult(...)那样做,而不管action方法返回的实际行为结果如何? – 2010-05-19 13:28:29

+0

没关系,如果他们期待部分视图。如果他们击中/报告/索引,虽然他们不会喜欢回头看裸露的partialview。我已经用我使用反射所取得的进展更新了我的问题。 – Nick 2010-05-19 13:52:01

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属性,因此您不必费心去确定它。 – 2013-11-26 16:39:46

相关问题