How to Inject Service into Custom Filter Attribute MVC

1.5k views Asked by At

I have a Custom Logging Attribute as below:

public class LoggerAttribute: ActionFilterAttribute
    {

        private readonly IHttpLogService _httpLogService;
        private readonly ILogService _logService;
        public LoggerAttribute(IHttpLogService httpLogService, ILogService logService)
        {
            _httpLogService = httpLogService;
            _logService = logService;
        }

        public override void OnActionExecuting(ActionExecutingContext filterContext)
        {
            LogDetails(filterContext);
        }

        private void LogDetails(ActionExecutingContext filterContext)
        {
            try
            {
                HttpLogService httpService = new HttpLogService();
                var httplogger = new LogMetaData()
                {
                    RequestParams = filterContext,
                    ResponseParams  = filterContext
                };
                _httpLogService.Emit("source", "", "Name", httplogger);
            }
            catch (Exception ex)
            {
                _logService.Emit(Core.Enums.LogLevel.Error, "token", "Error encountered while trying to execute the request.", ex);
                throw new Exception("An error occurred. Please try again later.");
            }
        }
    }

Following is the code from controller action method from where I need to execute the above filter, but the below code doesn't work, because I am not sure how to pass the service through the attribute:

[LoggerAttribute]
public int testMethod(RequestObject obj)
{
-----
}

The IHttpLogService & ILogService are the one that I need to inject into my custom filter attribute. But I am not quite sure how I can do this. Can someone please help me out with this?

2

There are 2 answers

0
Jesse de Wit On

You will have to separate the attribute from the actionfilter. Keep the attribute plain and simple:

[AttributeUsage(AttributeTargets.Method)]
public class LoggerAttribute : Attribute
{
}

And let your actionfilter do the magic:

public class LoggerActionFilter : IActionFilter
{
    // Can inject stuff in the constructor

    public void OnActionExecuting(ActionExecutingContext context)
    {
        // You may need some more null checking here...
        var attribute = ((ReflectedActionDescriptor)filterContext.ActionDescriptor).MethodInfo.GetCustomAttributes(true).OfType<LoggerAttribute>().FirstOrDefault();
        if (attribute is null)
        {
            return;
        }

        // Work your logger magic here.
    }
}

Don't forget to register your actionfilter at startup:

services.AddControllersWithViews(options =>
{
    options.Filters.Add(typeof(LoggerActionFilter));
});

Even better: you can use an IAsyncActionFilter now!

0
sayah imad On

According to my first analysis your code is not supposed to compile, what you are trying to use is known as filter in ASP.Net .

Filter Definition :

ASP.NET MVC Filter is a custom class where you can write custom logic to execute before or after an action method executes. Filters can be applied to an action method or controller in a declarative or programmatic way. Declarative means by applying a filter attribute to an action method or controller class and programmatic means by implementing a corresponding interface .

documention source

1 - First Step : (Create your filter)

public class TestFilter : ActionFilterAttribute
{
    private readonly ILoggerService loggerService;

    public TestFilter():this(new LoggerService())
    {

    }

    public TestFilter(ILoggerService loggerService)
    {
        this.loggerService = loggerService;
    }
    
    public override void OnActionExecuting(ActionExecutingContext filterContext)
    {
        base.OnActionExecuting(filterContext);
    }

    public override void OnActionExecuted(ActionExecutedContext filterContext)
    {
        base.OnActionExecuted(filterContext);
    }
}

2 - Second Step :(Register your filter)

public static void RegisterGlobalFilters(GlobalFilterCollection filters)
{
    filters.Add(new HandleErrorAttribute());
    filters.Add(new TestFilter());
}

3 - Thirs Step :(Integrate you filter to your Controller or Action)

[TestFilter]
public class CustomerController : Controller
{
    .....
}

In the first step i have instantiate my logger manually but in your case you are supposed to resolve your logger automticly throught your interface, what you can do is to replace this piece of code by your own code.