Create the validation behavior
public class ValidationBehaviour<T> : BasicBehavior where T : class
{
private readonly IContinuationDirector continuationDirector;
private readonly BehaviorGraph behaviorGraph;
private readonly IFubuRequest fubuRequest;
private readonly IValidator<T> validator;
public ValidationBehaviour(IContinuationDirector continuationDirector, BehaviorGraph behaviorGraph, IFubuRequest fubuRequest, IValidator<T> validator)
: base(PartialBehavior.Ignored)
{
this.continuationDirector = continuationDirector;
this.behaviorGraph = behaviorGraph;
this.fubuRequest = fubuRequest;
this.validator = validator;
}
protected override DoNext performInvoke()
{
var inputModel = fubuRequest.Get<T>();
var validationResult = validator.Validate(inputModel);
if (validationResult.IsValid)
{
return DoNext.Continue;
}
fubuRequest.Set(validationResult);
var actionCall = GetActionCallFromBehaviorGraph();
continuationDirector.TransferToCall(actionCall);
return DoNext.Stop;
}
private ActionCall GetActionCallFromBehaviorGraph()
{
return behaviorGraph
.Behaviors
.Where(chain => chain.FirstCall().HandlerType.Namespace == typeof(T).Namespace && chain.Route.AllowedHttpMethods.Contains(WebRequestMethods.Http.Get))
.Select(chain => chain.FirstCall())
.First();
}
}
Tell FubuMvc when to use the validation behavior
public class ValidationConfiguration : IConfigurationAction
{
public void Configure(BehaviorGraph graph)
{
graph.Actions()
.Where(x => x.HasInput && ObjectFactory.Model.HasDefaultImplementationFor(typeof(IValidator<>).MakeGenericType(x.InputType())))
.Each(x => x.AddBefore(new Wrapper(typeof(ValidationBehaviour<>).MakeGenericType(x.InputType()))));
}
}
Add an HtmlConvention to display the error messages
HtmlConvention(x => x.Editors.Always.Modify((request, tag) =>
{
var fubuRequest = request.Get<IFubuRequest>();
var validationResult = fubuRequest.Get<ValidationResult>();
if (validationResult.IsValid) return;
var ul = new HtmlTag("ul");
var liTags = validationResult.Errors.Where(error => error.PropertyName == request.Accessor.InnerProperty.Name).Select(vf => new HtmlTag("li", li => li.Text(vf.ErrorMessage)));
ul.Append(liTags);
tag.Append(ul);
}));
Apply the Validation Configuration
ApplyConvention<ValidationConfiguration>();
Wire up the IContinuationDirector and Validators
FubuApplication.For<ConfigureFubuMvc>().StructureMapObjectFactory(container =>
{
container.Scan(scanner =>
{
scanner.TheCallingAssembly();
scanner.WithDefaultConventions();
scanner.ConnectImplementationsToTypesClosing(typeof(IValidator<>));
});
container.For<IContinuationDirector>().Use<ContinuationHandler>();
})
.Bootstrap(RouteTable.Routes);