Saturday, 30 April 2016

Integrating Fluent Validation in Web API using Autofac

Create New Project

Install Necessary Packages

  1. Install-Package Autofac
  2. Install-Package Autofac.WebApi2
  3. Install-Package FluentValidation
  4. Install-Package FluentValidation.WebApi
  5. Install-Package Microsoft.AspNet.WebApi
  6. Install-Package Microsoft.AspNet.WebApi.Owin
  7. Install-Package Microsoft.Owin.Host.SystemWeb
  8. Install-Package Owin
  9. Install-Package Newtonsoft.Json

Create Partial Startup Classes

Partial class is just splitting class file into two or more files, and all parts are combined when the application is compiled.

It is used in a situation

  • When working on large projects, spreading a class over separate files enables multiple programmers to work on it at the same time.
  • When working with automatically generated source, code can be added to the class without having to recreate the source file. Visual Studio uses this approach when it creates Windows Forms, Web service wrapper code, and so on. You can create code that uses these classes without having to modify the file created by Visual Studio.
  • To split a class definition, use the partial keyword modifier

While adding this class our project structure looks like.

Startup.Autofac.cs

using Autofac;
using Autofac.Features.ResolveAnything;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
namespace FluentAutofacValidationAPI.App_Start
{
public partial class Startup
{
private static IContainer ConfigureAutofac()
{
var builder = new ContainerBuilder();
builder.RegisterModule<AutofacWebModule>();
builder.RegisterSource(new AnyConcreteTypeNotAlreadyRegisteredSource());
var container = builder.Build();
return container;
}
}
}

Startup.WebApi.cs

using Autofac;
using Autofac.Features.ResolveAnything;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
namespace FluentAutofacValidationAPI
{
public partial class Startup
{
private static IContainer ConfigureAutofac()
{
var builder = new ContainerBuilder();
builder.RegisterModule<AutofacWebModule>();
builder.RegisterSource(new AnyConcreteTypeNotAlreadyRegisteredSource());
var container = builder.Build();
return container;
}
}
}

Startup.cs

using Autofac;
using Autofac.Features.ResolveAnything;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
namespace FluentAutofacValidationAPI
{
public partial class Startup
{
private static IContainer ConfigureAutofac()
{
var builder = new ContainerBuilder();
builder.RegisterModule<AutofacWebModule>();
builder.RegisterSource(new AnyConcreteTypeNotAlreadyRegisteredSource());
var container = builder.Build();
return container;
}
}
}
view raw Startup.cs hosted with ❤ by GitHub

Add Infrastructure classes

Now our Project structure looks like

AutofacValidatorFactory.cs

using Autofac;
using FluentValidation;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
namespace FluentAutofacValidationAPI.Infrastructure
{
public class AutofacValidatorFactory : ValidatorFactoryBase
{
private readonly IComponentContext _context;
public AutofacValidatorFactory(IComponentContext context)
{
_context = context;
}
public override IValidator CreateInstance(Type validatorType)
{
object instance;
if (_context.TryResolve(validatorType, out instance))
{
var validator = instance as IValidator;
return validator;
}
return null;
}
}
}

AutofacWebModule.cs

using Autofac;
using Autofac.Integration.WebApi;
using FluentValidation;
using FluentValidation.WebApi;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Http.Validation;
namespace FluentAutofacValidationAPI.Infrastructure
{
public class AutofacWebModule : Module
{
protected override void Load(ContainerBuilder builder)
{
builder.RegisterApiControllers(ThisAssembly);
builder.RegisterAssemblyTypes(ThisAssembly)
.Where(t => t.Name.EndsWith("Validator"))
.AsImplementedInterfaces()
.InstancePerLifetimeScope();
builder.RegisterType<FluentValidationModelValidatorProvider>().As<ModelValidatorProvider>();
builder.RegisterType<AutofacValidatorFactory>().As<IValidatorFactory>().SingleInstance();
base.Load(builder);
}
}
}

ValidateFilterAttribute.cs

using System.Linq;
using System.Net;
using System.Net.Http;
using System.Web.Http.Controllers;
using System.Web.Http.Filters;
namespace FluentAutofacValidationAPI.Infrastructure
{
public class ValidateFilterAttribute : ActionFilterAttribute
{
public override void OnActionExecuting(HttpActionContext actionContext)
{
if (!actionContext.ModelState.IsValid)
{
actionContext.Response =
actionContext.Request.CreateErrorResponse(HttpStatusCode.BadRequest, actionContext.ModelState);
}
else if (actionContext.ActionArguments.FirstOrDefault().Value == null)
{
actionContext.ModelState.AddModelError("Model Null", "Form is empty. Please enter some value");
actionContext.Response =
actionContext.Request.CreateErrorResponse(HttpStatusCode.BadRequest, actionContext.ModelState);
}
base.OnActionExecuting(actionContext);
}
}
}

Add Modal Validation and Controller

Now our Project structure looks like

Controller

using FluentAutofacValidationAPI.Infrastructure;
using FluentAutofacValidationAPI.Models;
using System.Web.Http;
namespace FluentAutofacValidationAPI.Controllers
{
[RoutePrefix("api/people")]
public class PeopleController : ApiController
{
[HttpPost]
[Route("create")]
[ValidateFilter]
public IHttpActionResult CreatePerson(Person person)
{
return Ok("ok");
}
}
}

Model and Model Validation

using FluentValidation;
namespace FluentAutofacValidationAPI.Models
{
public class PersonValidator : AbstractValidator<Person>
{
public PersonValidator()
{
RuleFor(x => x.FirstName).NotEmpty();
RuleFor(x => x.LastName).NotEmpty();
}
}
public class Person
{
public string FirstName { get; set; }
public string OtherNames { get; set; }
public string LastName { get; set; }
}
}
view raw Person.cs hosted with ❤ by GitHub

Finally test our code




Thanks,


Download Code

1 comment:

  1. Hi... If you add a dependency in the validator constructor (PersonValidator), it throws a exception:

    An exception of type 'Autofac.Core.DependencyResolutionException' occurred in Autofac.dll but was not handled in user code

    Additional information: None of the constructors found with 'Autofac.Core.Activators.Reflection.DefaultConstructorFinder' on type 'FluentAutofacValidationAPI.Models.PersonValidator' can be invoked with the available services and parameters:

    Cannot resolve parameter 'FluentAutofacValidationAPI.Infrastructure.IStudentService service' of constructor 'Void .ctor(FluentAutofacValidationAPI.Infrastructure.IStudentService)'.

    ReplyDelete