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: