The self-reliant use ASP.NET Core filter attributes defined injection MVC

Original: the ASP.NET MVC Core use a custom filter attribute dependency injection

  In addition to adding to the own middleware application ASP.NET MVC Core pipe outside, you can also use custom attributes to control MVC filter response, and selectively apply them to the entire operation of the controller or controllers.

  One commonly used MVC ASP.NET Core filter is   ExceptionFilterAttribute, for processing error response Wep API application. It is very easy to implement, developers, and I use the MVC problems faced by the filter properties in ASP.NET Core is injected access Startup.cs class components. These are usually configured, the environment or logging.

  Dependency injection is usually a very useful object usage, such as the above-mentioned IEnvironment , IConfiguration and ILogger <T>, is different behavior MVC filter properties you are achievable. Based on these values, the behavior of your property may be different. For example, you do not want to stack trace and error details to the public Production Web API service error response, especially in the service endpoint is a common situation. You want to do this only for the isolation of development and staging environment.

Filter properties exemplary MVC #

  In a custom ExceptionFilterAttrubute simple example of a class, I'll show you how to use dependency injection objects in a custom attribute. Let us begin from the code.

Copy the code
    public class ExceptionMessage
    {
        private object errorMessage;
        public string Message { get; private set; }
        public string Description { get; private set; }
        public IDictionary<string,string> ValidationErrors { get; private set;}
        public ExceptionMessage(ExceptionContext context)
        {
            if (context.ModelState != null && context.ModelState.Any(m => m.Value.Errors.Any()))
            {
                this.Message = "Model validation failed.";
                this.ValidationErrors = context.ModelState.Keys
                    .SelectMany(key => context.ModelState[key].Errors.ToDictionary(k => key, v => v.ErrorMessage))
                    .ToDictionary(k => k.Key, v => v.Value);
            }
            else
            {
                this.Message = context.Exception.Message;
                this.Description = context.Exception.StackTrace;
            }
        }
    }
Copy the code

As this document is not an error message structure, but in Microsoft REST API guidelines  Github repository provides some guidelines in the error message, which may give you help.

Now your error response is a JSON Ideally news, but let's serialized left to the application of the pipeline and returns a ObjectResponse derived instance. For this reason I created ErrorObjectResult .

Copy the code
using Microsoft.AspNetCore.Mvc;
using System.Net;

namespace CzarCms.Models
{
    public class ErrorObjectResult : ObjectResult
    {
        public ErrorObjectResult(object value, HttpStatusCode statusCode = HttpStatusCode.InternalServerError) : base(value)
        {
            StatusCode = (int)statusCode;
        }
    }
}
Copy the code

In addition to obtaining the status code constructor (the default is 500 Internal Server Error), and ObjectResponse than the object infrastructure function, there is nothing special in this class. The core component of our focus in this article is our custom ExceptionFilterAttribute derived class.

Copy the code
public class ApiExceptionFilter : ExceptionFilterAttribute
    {
        public override void OnException(ExceptionContext context)
        {
            var errorMessage = new ExceptionMessage(context);
            if (context.ModelState.ErrorCount==0)
                context.Result = new ErrorObjectResult(errorMessage);
            else
                context.Result = new ErrorObjectResult(errorMessage,HttpStatusCode.BadRequest);
            base.OnException(context);
        }
    }
Copy the code

 Let us use this property to decorate our controller to handle the error it may occur.

Copy the code
// GET api/values
        [HttpGet]
        [ApiExceptionFilter]
        public IEnumerable<string> Get()
        {
            return new string[] { "value1", "value2" };
        }
Copy the code

 Set dependency injection #

  We first need to visit us in the MVC filter properties Startup class in the three interface settings dependency injection.

Copy the code
    public class Startup
    {
        public IConfiguration Configuration { get; private set; }
        public IHostingEnvironment HostingEnvironment { get; private set; }

        public Startup(IConfiguration configuration, IHostingEnvironment env)
        {
            Configuration = configuration;
            HostingEnvironment = env;
            ILogger Logger = new LoggerFactory()
                .AddConsole()
                .AddDebug()
                .CreateLogger(typeof(Program));
        }

        public void ConfigureServices(IServiceCollection services)
        {
            services.AddSingleton<IConfiguration>(new ConfigurationBuilder()
                .SetBasePath(Directory.GetCurrentDirectory())
                .AddJsonFile($"appsettings.{this.HostingEnvironment.EnvironmentName.ToLower()}.json")
                .Build());
            services.AddLogging();
            services.AddMvc();

            services.AddScoped<ApiExceptionFilter>();
        }

        public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
        {
            loggerFactory.AddConsole();
            app.UseMvc();
        }
    }
Copy the code

You can see that we will add to our DI ApiExceptionFilter scope of services. This is because we will refer to it in different ways on the controller, so as to initialize it by the constructor dependency injection with the new parameters.

We have in the Startup type of ConfigureServices method of injecting our IEnvironment and ILogger , but we can not access it in the properties. If we use IEnvironment ILogger parameters and add constructor property, we will get a compilation error, because you can not use [ApiExceptionFilter] decorative Controller / Action, because it now needs to interface through IEnvironment and ILogger. To do this, we use with attributes ServiceFilter controller to decorate   it, we will   ApiExceptionFilter class type as a constructor argument.

Copy the code
[HttpGet]
[ServiceFilter(typeof(ApiExceptionFilter))]
public IEnumerable<string> Get()
{
  return new string[] { "value1", "value2" };
}
Copy the code

Finally, we must update the constructor MVC filter attributes to accept IEnvironment, IConfiguration and ILogger parameters.

Copy the code
public class ApiExceptionFilter : ExceptionFilterAttribute
    {
        private ILogger<ApiExceptionFilter> logger;
        private IHostingEnvironment environment;
        private IConfiguration configuration;
        public ApiExceptionFilter(IHostingEnvironment environment, IConfiguration configuration, ILogger<ApiExceptionFilter> logger)
        {
            this.environment = environment;
            this.configuration = configuration;
            this.logger = logger;
        }
        public override void OnException(ExceptionContext context)
        {
            var errorMessage = new ExceptionMessage(context);
            if (context.ModelState.ErrorCount==0)
                context.Result = new ErrorObjectResult(errorMessage);
            else
                context.Result = new ErrorObjectResult(errorMessage,HttpStatusCode.BadRequest);
            base.OnException(context);
        }
    }
Copy the code

We injected IEnvironment and ILogger <T> in the custom attribute class in the filter. From Microsoft.AspNetCore.Mvc.Filters namespace any operation of the filter properties may be any class derived using the same method.

Guess you like

Origin www.cnblogs.com/lonelyxmas/p/12052098.html