Reputation: 5366
I am required to record user action and I don't want to have code in every method is every controller so would it make sense to somehow do this in the basecontroller ? OR is there a better way?
public class BaseController : Controller
{
protected ILogger logger;
public BaseController(ILogger<BaseController> logger)
{
this.logger = logger;
}
public override void OnActionExecuting(ActionExecutingContext context)
{
//How do I get the current controller?
//How do I get the current method being called?
//How can I pass in additional parameters?
//How can I get the user?
logger.LogWarning("Loaded BaseController");
base.OnActionExecuting(context);
}
}
Upvotes: 1
Views: 2862
Reputation: 119
For getting controller & action names you can use ActionDescriptor
of ActionExecutingContext
protected override void OnActionExecuting(ActionExecutingContext filterContext)
{
var descriptor = filterContext.ActionDescriptor;
var actionName = descriptor.ActionName;
var controllerName = descriptor.ControllerDescriptor.ControllerName;
......
base.OnActionExecuting(filterContext);
}
Regarding User information: controller initialisation will occur before authorisation takes place. So all of yours controllers will be created before any OnAuthorization takes place.
Approach to deal with these situations is to use Action Filters. The Authorize Attribute is fired early than controller initialisation occur.
Have a look this articles:
Upvotes: 1
Reputation: 38638
There are many ways to do that.
First: You could create your own base controller and implement OnActionExecution
as you did. See the sample bellow to get information from ActionExecutingContext
.
If you go this way, every controller that inhirits from this base controller will get the implementation of the logger because you are overriding OnActionExecuting
(that applies to all actions of your controller).
public override void OnActionExecuting(ActionExecutingContext context)
{
//How do I get the current controller?
string controllerName = context.ActionDescriptor.ControllerDescriptor.ControllerName
//How do I get the current method being called?
string actionName = context.ActionDescriptor.ActionName;
//How can I pass in additional parameters?
foreach (var parameter in context.ActionParameters)
{
var parameterKey = parameter.Key;
var parameterValue = parameter.Value;
}
//How can I get the user?
var user = this.User; // IPrinciple instance, explore this object
logger.LogWarning("Loaded BaseController");
base.OnActionExecuting(context);
}
Second: On the other hand, you can use ActionFilters which is a class that inhirits from ActionFilter
class and do the same implementation on this classe overriding the OnActionExecuting
. Then you can decorate your controllers with this attribute to make the logger. Given it is an attribute, you have to define the name fo the class with a sufix Attribute
and use without it. For sample:
public class LoggerAttribute : ActionFilterAttribute
{
public override void OnActionExecuting(ActionExecutingContext context)
{
// same code above
}
}
[Logger]
public class CustomerController : Controller
{
// actions code...
}
Third: Use the same action filter class and instead of applying on all classes you want, you define it as a global action filter and it will be applied to all controllers. You have to define it on GlobalFilter
and if you are using the default template of asp.net mvc, you can define it on the FilterConfig.cs
, for sample:
filters.Add(new LoggerAttribute());
Upvotes: 5