11
votes

Puis-je obtenir le type de retour d'une action à partir d'un filtre d'action?

J'ai une application ASP.NET MVC 2 dans laquelle je crée un filtre d'action personnalisé. Ce filtre est assis sur les contrôleurs de l'application et vérifie à partir de la base de données si cette fonction est actuellement disponible.

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


0 commentaires

4 Réponses :


0
votes

à l'heure OnAptionExecutation est invoqué, la méthode d'action n'a pas encore été exécutée, il n'ya donc aucun moyen que vous sachiez si cette méthode d'action va retourner quelle sous-classe de ActionResult . Ainsi, à moins que vous ne puissiez aller avec la mise en œuvre de l'analyse CIL (que je pense peut devenir laid très rapidement), je ne pense pas ce que vous voulez faire est possible.

Cela dit, n'est-ce pas le fait que vous redirigez les utilisateurs sur une vue lorsque le contrôleur n'est pas suffisamment disponible? Je veux dire, je ne comprends pas pourquoi vous souhaitez rediriger les utilisateurs vers un résultat JSON ou une vue partielle.


6 commentaires

Le site est un portail pour nos clients. J'ai des pages telles que la page d'accueil avec des vues partielles à partir d'autres contrôleurs. Je veux retourner une vue partielle avec un message dans la vue du parent. Le contrôleur domestique sera toujours disponible mais le contrôleur de rapports peut ne pas être. Le widget de rapports devrait simplement montrer un message poli.


@Nick: Alors pourquoi ne pas simplement faire quelque chose comme FilterContext.result = New PartalViewResult (...), quel que soit le résultat actuel d'action à renvoyer par la méthode d'action?


C'est bon s'ils attendent la vue partielle. S'ils ont touché / rapports / index, bien qu'ils ne souhaitaient pas une vision partielle nue. J'ai mis à jour ma question avec les progrès que j'ai faits à l'aide de la réflexion.


J'ai également pensé à l'approche de réflexion aussi mais cela ne fonctionne que si vous déclarez strictement votre méthode d'action pour renvoyer une sous-classe d'action spécifique. Cela pourrait ne pas toujours être possible, car vous pourriez avoir un code conditionnel, comme si (COND1) retourne JSON (...); sinon retourner JavaScript (...); De plus, il est presque une convention pour simplement utiliser ActionResulte, quelle que soit la sous-classe spécifique renvoyée. Cela dit, si vous pouvez vivre avec ces contraintes connues que la réflexion est une option.


@NICK: Concernant le message mis à jour: Pour choisir correctement la méthode, parmi de nombreuses surcharges différentes, vous devez suivre les étapes de résolution que MVC fait pour trouver la correspondance. Spécifiquement, vous devriez examiner la méthode FindactionMethod de la classe interne ActionMethodSelector (dans le code source MVC 2). Si vous avez de la chance, vous pourrez peut-être réutiliser la méthode sans beaucoup de changement et de nombreuses dépendances - je ne l'ai pas essayée moi-même.


Merci pour cela, j'ai téléchargé la source et j'ai eu une bonne fouet. Il semble utiliser une méthode très similaire à la réponse que j'ai proposée :)



2
votes

OK, c'est la solution que j'ai proposée.

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


0 commentaires

9
votes

J'ai créé un authentificationFilterattribute basé sur ce qui renvoie différents résultats basés sur le type:

    /// <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 commentaires

Solution intéressante, merci. Notez que si filtercontext.acteDescriptor est de type system.web.mvc.refledyActionDescriptor, il aura déjà la propriété MethodInfo afin que vous n'ayez pas besoin d'aller à la peine de le déterminer.



0
votes

Quelques belles réponses ci-dessus, mais dans le noyau MVC, je remarque que vous pouvez simplement obtenir les informations méthodes en casting à un contrôleurDescriptor, qui nettoierait les réponses ci-dessus. (J'utilise cela dans un blazor Webassembly Web API Backend d'API dans l'aperçu 6) EM>

Type t = actionContext.GetReturnType();


0 commentaires