ASP.net Core、使っていると、もれなくDI「dependency injection」がついてくるよね。
ちゃんと使っている?
でも、Contorllerのアクションでよく使う、バリデーションのActionFilterAttributeってそのままでは、DIできないですよね!
こんな感じで実装して
TestFilterAttribute.cs
using System.Linq;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.Filters;
using Microsoft.AspNetCore.Mvc.ModelBinding;
namespace Attribute.Attribute
{
public class TestFilterAttribute : ActionFilterAttribute
{
public override void OnActionExecuting(ActionExecutingContext actionContext)
{
if (!actionContext.ActionArguments.All(x => x.Key != "id" || CheckValue(x.Value as int?)))
{
actionContext.Result = new BadRequestObjectResult(new ModelStateDictionary());
}
}
private bool CheckValue(int? value)
{
if (value == null) return false;
if (value < 1) return false;
return true;
}
}
}
こんな感じで呼び出す
TestController.cs
using Attribute.Attribute;
using Attribute.Model;
using Microsoft.AspNetCore.Mvc;
namespace Attribute
{
public class TestController : Controller
{
public TestController()
{
}
// GET: HomeController
public ActionResult Index()
{
return View(new TestViewModel());
}
// GET: HomeController/Details/5
[TestFilter]
public ActionResult Details(int id)
{
return View(new TestViewModel(){Id = id});
}
}
}
[TestFilter]という形で呼び出すんだからDIできないんだけれど。
さすがの、.net
IFilterFactory というインターフェースがありました
こいつを継承すると
まず、TestFilterAttributeにインターフェースを追加
TestFilterAttribute
using System.Linq;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.Filters;
using Microsoft.AspNetCore.Mvc.ModelBinding;
namespace Attribute.Attribute
{
public class TestFilterAttribute : ActionFilterAttribute, ITestFilterAttribute
{
public override void OnActionExecuting(ActionExecutingContext actionContext)
{
if (!actionContext.ActionArguments.All(x => x.Key != "id" || CheckValue(x.Value as int?)))
{
actionContext.Result = new BadRequestObjectResult(new ModelStateDictionary());
}
}
private bool CheckValue(int? value)
{
if (value == null) return false;
if (value < 1) return false;
return true;
}
}
}
ITestFilterAttribute.cs
using Microsoft.AspNetCore.Mvc.Filters;
namespace Attribute.Attribute
{
public interface ITestFilterAttribute : IActionFilter
{
}
}
Startupに追加
services.AddScoped();
Startup.cs
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.HttpsPolicy;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Attribute.Attribute;
namespace Attribute
{
public class Startup
{
public Startup(IConfiguration configuration)
{
Configuration = configuration;
}
public IConfiguration Configuration { get; }
// This method gets called by the runtime. Use this method to add services to the container.
public void ConfigureServices(IServiceCollection services)
{
services.AddRazorPages();
services.AddScoped<ITestFilterAttribute, TestFilterAttribute>();
}
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
else
{
app.UseExceptionHandler("/Error");
// The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts.
app.UseHsts();
}
app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseRouting();
app.UseAuthorization();
app.UseEndpoints(endpoints =>
{
endpoints.MapControllerRoute(name: "default", pattern: "{controller=Permission}/{action=Index}/{id?}");
endpoints.MapRazorPages();
});
}
}
}
Factry、を追加して、そこから ITestFilterAttribute を呼び出す
TestFilterFactoryAttribute.cs
using System;
using Microsoft.AspNetCore.Mvc.Filters;
using Microsoft.Extensions.DependencyInjection;
namespace Attribute.Attribute
{
public class TestFilterFactoryAttribute : System.Attribute, IFilterFactory
{
public bool IsReusable => false;
public IFilterMetadata CreateInstance(IServiceProvider serviceProvider)
{
// DIのServiceProviderからITestFilterAttributeを取り出す
ITestFilterAttribute filter = serviceProvider.GetService<ITestFilterAttribute>();
return filter;
}
}
}
で、Factryの方を呼び出すように、Controllerを変更
TestController.cs
using Attribute.Attribute;
using Attribute.Model;
using Microsoft.AspNetCore.Mvc;
namespace Attribute
{
public class TestController : Controller
{
public TestController()
{
}
// GET: HomeController
public ActionResult Index()
{
return View(new TestViewModel());
}
// GET: HomeController/Details/5
[TestFilterFactory]
public ActionResult Details(int id)
{
return View(new TestViewModel(){Id = id});
}
}
}
という感じです
まあ、サービスプロバイダーとしての実装になっちゃうんですけど