Personally, I like to isolate business rules and/or validations outside of MVC Controllers. In this case, API Controllers. I use ActionFilterAttribute to define my checks on parameters being passed in my MVC Web API routes.
Here’s an example of a WebAPI route with parameter binding:
// GET: /1/employees/AA0000111" [Route("{WebServiceVersion}/employees/{employeeId}")] [ValidateEmployeeId] public IHttpActionResult GetUser(string employeeid, int WebServiceVersion = 1) { // GET: Do something with webServiceVersion value like logging. var user = _emprepository.GetUser(employeeid); return Content(HttpStatusCode.OK, user); }
I want to isolate validating employeeid outside of my controller for a couple of reasons:
1) Isolation – You may have multiple cases on validating your parameters. In this case, employeeId can be permutated in different ways specially because it is a string. Other developers can easily get lost on what the action controller is actually doing if you have long code that includes all various validations
2) Good development practice – I prefer to see nice clean code and separation on what my controllers do vs business rules
3) Testing – I can isolate testing on my controllers vs business rules. This is really the motivating factor for me.
That said, let’s take a look at the ActionFilterAttribute further. For more information on this, see:
(NOTE: There are 2 versions of ActionFilterAttribute)
When unit testing, make sure you’re writing the correct tests for your filter. In this case, I’m using the namespace: System.Web.Http.Filters
public class ValidateEmployeeIdAttribute : ActionFilterAttribute { public override void OnActionExecuting(HttpActionContext actionContext) { var employeeid = actionContext.ActionArguments["employeeid"].ToString(); if (string.IsNullOrEmpty(employeeid) || employeeid.ToLower() == "<somecheck>" || employeeid.ToLower() == "<replace and use other validation such as regex>") { actionContext.Response = actionContext.Request.CreateResponse(HttpStatusCode.BadRequest, $"Input parameter error, employeeId: {employeeid} - not specified, null or bad format", actionContext.ControllerContext.Configuration.Formatters.JsonFormatter); } base.OnActionExecuting(actionContext); } }
Note in the preceding code for the controller that I decorated the web api action method with: [ValidateEmployeeId]
This instruct the controller to use the custom ActionFilterAttribute that I created above
Testing your custom validate via UNIT Test/s:
For simplicity, I used MSTest that comes with visual studio.
[TestMethod, TestCategory("UserController")] public void Validate_EmpId_ActionFilterAttribute() { var mockactioncontext = new HttpActionContext { ControllerContext = new HttpControllerContext { Request = new HttpRequestMessage() }, ActionArguments = { { "employeeid", "<somecheck>" } } }; mockactioncontext.ControllerContext.Configuration = new HttpConfiguration(); mockactioncontext.ControllerContext.Configuration.Formatters.Add(new JsonMediaTypeFormatter()); var filter = new ValidateEmployeeIdAttribute(); filter.OnActionExecuting(mockactioncontext); Assert.IsTrue(mockactioncontext.Response.StatusCode == HttpStatusCode.BadRequest); }
At this point, you should have separation of code to validate your “validations” vs controller.
Using fiddler, I can see that whenever I submit a request that has an invalid value for employeeid, I get the correct response: