[WEB API]使用ActionFilter來統一處理ModelState的驗證
前言
當我們使用MVC或Web api的時候,其實我們常常都會在controller寫判斷驗證我們Model所定義的驗證,但是其實我們可以使用更好的方式來包裝,在此我們使用ActionFilter的方式,其實這個就是微軟幫我們的WebApi包裝好的AOP概念,接下來就說明一下實際情況
情境模擬
正常來說我們一旦使用了Web Api,我們的驗證就都會定義在Model裡面,這樣子如果我們只要有任何驗證相關的,只要去Model裡面看,示例如下
EmployeeModel
public class EmployeeModel
{
public int Id { get; set; }
[Required]
[MaxLength(5,ErrorMessage ="{0}不可超過5個字")]
public string Name { get; set; }
[Required]
public string Address { get; set; }
}
Controller
public IHttpActionResult Post(EmployeeModel employee)
{
if (!ModelState.IsValid)
{
return BadRequest(ModelState);
}
return Ok();
}
結果
但是正常來說我們很多Controller的Action都需要驗證Model,那如果我們能把這段重覆的程式碼,放在一個地方,以後有什麼相關的需要修改,就盡量改這個地方就好了,接下來就新增一支名為ValidateModelAttribute.cs
public class ValidateModelAttribute : ActionFilterAttribute
{
public override void OnActionExecuting(HttpActionContext actionContext)
{
if (actionContext.ModelState.SelectMany(x => x.Value.Errors).Any(x => !string.IsNullOrEmpty(x.ErrorMessage)))
{
actionContext.Response = actionContext.Request.CreateErrorResponse(HttpStatusCode.BadRequest, actionContext.ModelState);
}
base.OnActionExecuting(actionContext);
}
}
做完了之後我們可以取決於要把這個attribute掛在哪邊,可以掛在method或class的上面
掛在Method上面
public class ValuesController : ApiController
{
[ValidateModel]
public IHttpActionResult Post(EmployeeModel employee)
{
return Ok();
}
}
掛在Class上面
[ValidateModel]
public class ValuesController : ApiController
{
public IHttpActionResult Post(EmployeeModel employee)
{
return Ok();
}
}
最方便的方式就是直接掛全局的,這時候我們可以在Global.asax動點手腳,在適當的生命週期插進去我們想要處理的邏輯
Global.asax
public class WebApiApplication : System.Web.HttpApplication
{
protected void Application_Start()
{
AreaRegistration.RegisterAllAreas();
GlobalConfiguration.Configure(WebApiConfig.Register);
FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
RouteConfig.RegisterRoutes(RouteTable.Routes);
BundleConfig.RegisterBundles(BundleTable.Bundles);
RegisterWebApiFilters(GlobalConfiguration.Configuration.Filters); //我們新加入的Filter
}
public static void RegisterWebApiFilters(System.Web.Http.Filters.HttpFilterCollection filters)
{
filters.Add(new ValidateModelAttribute()); //加進剛剛寫的那支類別
}
}
至此我們就註冊了全局都要去使用這個ModelState.IsValid,就不用在類別或方法裡面在掛attribute了,不過如果我們想要忽略掉某個action不要去驗證Model的狀態呢?我們再新增一個IgnoreValidateModelAttribute的類別來濾掉吧
public class ValidateModelAttribute : ActionFilterAttribute
{
public override void OnActionExecuting(HttpActionContext actionContext)
{
if (actionContext.ActionDescriptor.GetCustomAttributes<IgnoreValidateModelAttribute>(false).Any()) //有掛上這個Attribute的,直接就return掉
{
return;
}
if (actionContext.ModelState.SelectMany(x => x.Value.Errors).Any(x => !string.IsNullOrEmpty(x.ErrorMessage)))
{
actionContext.Response = actionContext.Request.CreateErrorResponse(HttpStatusCode.BadRequest, actionContext.ModelState);
}
base.OnActionExecuting(actionContext);
}
}
public class IgnoreValidateModelAttribute : Attribute
{
}
接著再來剛剛那個類別,掛上Ignore來試試看,是不是就不會做驗證了
public class ValuesController : ApiController
{
[IgnoreValidateModel]
public IHttpActionResult Post(EmployeeModel employee)
{
return Ok("忽略掉這個方法");
}
}
測試結果
結論
簡單介紹一下ActionFilter的用法,其實還有很多情境可以應用,請自行發揮囉。