Fluent Validation allows you to separate validation rules from your model and helps you structure the rules so that they are nice and readable. The rules are also super easy to test.

Some existing code

Let’s say we have started to build a web api to add new customers:

[Route("api/Customers")]
public class CustomersController : Controller
{
    private CustomerData customerData;
    public CustomersController(CustomerData customerData)
    {
        this.customerData = customerData;
    }

    [HttpGet("{customerId}", Name = "CustomerGet")]
    public IActionResult Get(Guid customerId)
    {
        var customer = customerData.GetCustomerById(customerId);
        if (customer == null) return NotFound();

        return Ok(customer);
    }

    [HttpPost]
    public IActionResult Post([FromBody]NewCustomer newCustomer)
    {
        Guid customerId = customerData.AddCustomer(newCustomer);
        var addedCustomer = customerData.GetCustomerById(customerId);
        var url = Url.Link("CustomerGet", new { customerId = customerId });
        return Created(url, addedCustomer);
    }
}

Our NewCustomer model is:

public class NewCustomer
{
    public string CustomerType { get; set; }
    public string Title { get; set; }
    public string FirstName { get; set; }
    public string CustomerName { get; set; }
    public string EmailAddress { get; set; }
}

We also have an action filter which will handle validation on the models and return a 400 if they are invalid:

public class ValidatorActionFilter : IActionFilter
{
    public void OnActionExecuting(ActionExecutingContext filterContext)
    {
        if (!filterContext.ModelState.IsValid)
        {
            filterContext.Result = new BadRequestObjectResult(filterContext.ModelState);
        }
    }

    public void OnActionExecuted(ActionExecutedContext filterContext)
    {

    }
}

Startup.ConfigureServices looks like this to wire everything up:

public void ConfigureServices(IServiceCollection services)
{
    services.AddMvc(opt =>
    {
        opt.Filters.Add(typeof(ValidatorActionFilter));
    });
    services.AddScoped<CustomerData>();
}

So, let’s say we want to make CustomerName a required field in the POST method on the newCustomer model. We could use a standard data annotation:

[Required]
public string CustomerName { get; set; }

However, we’re not going to do this. Instead, we’re going to use Fluent Validation to keep our validation logic separate from the model and go on to add more complex rules …

#asp.net #api #.net

Fluent Validation in an ASP.NET Core Web API
4.75 GEEK