I have an ActionFilterAttribute on my application that is used to redirect users when it detects that the user is not authenticated. Within the filter, I would like to detect when the ReturnType for an action is a JsonResult.
As a workaround, I initially created a custom attribute of IsJsonResult, and decorated the JsonResult methods in my solution with that attribute. This works, and is implemented in the action filter as follows:
public class CheckUser : ActionFilterAttribute
{
public override void OnActionExecuting(ActionExecutingContext actionExecutingContext)
{
base.OnActionExecuting(actionExecutingContext);
object[] customAttributes = actionExecutingContext.ActionDescriptor.GetCustomAttributes(true);
bool isJsonResult = customAttributes.FirstOrDefault(a => a.GetType() == typeof(IsJsonResult)) != null;
if (isJsonResult)
{
return; // Don't perform any additional checks on JsonResult requests.
}
// Additional checking code omitted.
}
}
That works, but I dislike the idea of decorating all of the JsonResult actions in this project. That could easily fail if a new JsonResult gets added to the project, and we forget to decorate it accordingly.
Moreover, I can see that the ReturnType with a name of "JsonResult" is in the debugger within the actionExecutingContext object shown above. Here is the path shown in the watch window:
actionExecutingContext > ActionDescriptor > [System.Web.Mvc.ReflectedActionDescriptor] > MethodInfo > ReturnType > FullName
That FullName property has a value of "System.Web.Mvc.JsonResult".
So it would seem that I could extract that value directly from the actionExecutingContext object, and create a supporting method to return a bool indicator. To achieve this, here is the code that I've written.
private bool isReturnTypeJson(ActionExecutingContext actionExecutingContext)
{
string actionName = actionExecutingContext.ActionDescriptor.ActionName;
string controllerName = actionExecutingContext.ActionDescriptor.ControllerDescriptor.ControllerName;
Type controllerType = actionExecutingContext.Controller.GetType();
try
{
// Only effective when the actionName is not duplicated in the controller.
Type returnType = controllerType.GetMethod(actionName).ReturnType;
return (returnType.Name == "JsonResult");
}
catch (AmbiguousMatchException)
{
// Using LINQ, can I filter this collection to isolate just the methods
// that have the same name as the "actionName" variable above?
MethodInfo[] methodsInfoCollection = controllerType.GetMethods(BindingFlags.Public | BindingFlags.Instance);
// Attempted code from https://stackoverflow.com/questions/15283158/net-mvc-counting-action-methods-in-web-application
//var info = typeof(controllerType)
// .Assembly.GetTypes()
// .Where(t => typeof(Controller).IsAssignableFrom(t))
// .Where(t => t.Namespace.StartsWith("AwesomeProduct.Web"))
// .SelectMany(t => t.GetMethods(BindingFlags.Public | BindingFlags.Instance))
// .Where(m => typeof(ActionResult).IsAssignableFrom(m.ReturnType))
// .Where(m => !m.IsAbstract)
// .Where(m => m.GetCustomAttribute<NonActionAttribute>() == null);
}
return false;
}
Assigning the returnType works in the try-block whenever the action name is unique in the controller. But if there are multiple instances of the same action name in the controller, then the AmbiguousMatchException occurs. So in the catch-block, I've assigned the Methods to a collection. Using LINQ, how can I filter values from the methodsInfoCollection variable to the action that is arriving via the ActionExecutingContext?
Some of the articles that I've researched are below, and have used some ideas from those. But I've yet to figure this out.
Thanks for your help.
==============
Update to original code. This works, but the looping is not ideal.
private bool isReturnTypeJson(ActionExecutingContext actionExecutingContext)
{
string actionName = actionExecutingContext.ActionDescriptor.ActionName;
string controllerName = actionExecutingContext.ActionDescriptor.ControllerDescriptor.ControllerName;
Type controllerType = actionExecutingContext.Controller.GetType();
try
{
// Only effective when the actionName is not duplicated in the controller.
Type returnType = controllerType.GetMethod(actionName).ReturnType;
return (returnType.Name == "JsonResult");
}
catch (AmbiguousMatchException)
{
// Using LINQ, can I filter this collection to isolate just the methods with a name of actionName.
MethodInfo[] methodInfoCollection = controllerType.GetMethods(BindingFlags.Public | BindingFlags.Instance);
foreach (MethodInfo methodInfo in methodInfoCollection)
{
if (methodInfo.ReturnType != null) {
if (methodInfo.ReturnType == typeof(ActionResult))
{
return false;
}
if (methodInfo.ReturnType == typeof(JsonResult))
{
return true;
}
}
}
}
return false;
}
See the solution from Reza Aghaei below. His solution totally eliminates the need for a separate method.
You can use
((ReflectedActionDescriptor)filterContext.ActionDescriptor).MethodInfo.ReturnType
And here is what I tried to check if return type is JsonResult
:
((ReflectedActionDescriptor)filterContext.ActionDescriptor).MethodInfo.ReturnType == typeof(JsonResult)
And it returns true for my action:
public JsonResult Index()
{
return Json(new { });
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With