我有一个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,我可以找出最初请求的操作的返回类型是什么吗?
编辑:
好的,我越来越近但又有另一个问题。
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方法中的信息,无论如何都要确定正在调用的重载更精确吗?
答案 0 :(得分: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 :(得分: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(string)调用无法识别请求的方法,我将从ActionExecutingContext.ActionParameters集合中获取参数集合,并构建请求中传递的参数类型的数组。然后,我可以使用Type.GetMethod(string,type())重载来更具体地说明我的请求。
答案 2 :(得分:0)
当调用OnActionExecuting
时,操作方法尚未执行,因此您无法知道该操作方法是否将返回ActionResult
的哪个子类。所以,除非你可以使用CIL分析实现(我觉得很快就会变丑),我不认为你想做什么是可能的。
那就是说,当控制器不够用时,是不是将用户重定向到视图?我的意思是,我不明白为什么要将用户重定向到JSON结果或部分视图。
答案 3 :(得分:0)
上面有一些不错的答案,但在 MVC Core 中,我注意到您可以通过强制转换为 ControllerActionDescriptor 来获取方法信息,这会清除上面的一些答案。 (我在预览版 6 的 Blazor Webassembly Web Api 后端中使用它)
((Microsoft.AspNetCore.Mvc.Controllers.ControllerActionDescriptor)filterContext.ActionDescriptor).MethodInfo
因此获取返回类型可能会变成:
public static class FilterHelper
{
public static Type GetReturnType(this ActionExecutingContext filterContext)
{
return ((ControllerActionDescriptor)filterContext.ActionDescriptor).MethodInfo.ReturnType;
}
}
并像这样使用:
Type t = actionContext.GetReturnType();
将更新为 6 Preview 的完整解决方案