|
| 1 | +# FluentValidation Plugin for SlimMessageBus <!-- omit in toc --> |
| 2 | + |
| 3 | +Please read the [Introduction](intro.md) before reading this provider documentation. |
| 4 | + |
| 5 | +- [Introduction](#introduction) |
| 6 | +- [Configuration](#configuration) |
| 7 | + - [Configuring FluentValidation](#configuring-fluentvalidation) |
| 8 | + - [Custom exception](#custom-exception) |
| 9 | + - [Producer side validation](#producer-side-validation) |
| 10 | + - [Consumer side validation](#consumer-side-validation) |
| 11 | + - [Configuring without MSDI](#configuring-without-msdi) |
| 12 | + |
| 13 | +## Introduction |
| 14 | + |
| 15 | +The [`SlimMessageBus.Host.FluentValidation`](https://www.nuget.org/packages/SlimMessageBus.Host.FluentValidation) introduces validation on the producer or consumer side by leveraging the [FluentValidation](https://www.nuget.org/packages/FluentValidation) library. |
| 16 | +The plugin is based on [`SlimMessageBus.Host.Interceptor`](https://www.nuget.org/packages/SlimMessageBus.Host.Interceptor) core interfaces and can work with any transport including the memory bus. |
| 17 | + |
| 18 | +See the [full sample](/src/Samples/Sample.ValidatingWebApi/). |
| 19 | + |
| 20 | +## Configuration |
| 21 | + |
| 22 | +Consider the following command, with the validator (using FluentValidation) and command handler: |
| 23 | + |
| 24 | +@[:cs](../src/Samples/Sample.ValidatingWebApi/Commands/CreateCustomerCommand.cs,Example) |
| 25 | + |
| 26 | +@[:cs](../src/Samples/Sample.ValidatingWebApi/Commands/CreateCustomerCommandValidator.cs,Example) |
| 27 | + |
| 28 | +@[:cs](../src/Samples/Sample.ValidatingWebApi/Commands/CreateCustomerCommandHandler.cs,Example) |
| 29 | + |
| 30 | +### Configuring FluentValidation |
| 31 | + |
| 32 | +Consider an in-process command that is delivered using the memory bus: |
| 33 | + |
| 34 | +@[:cs](../src/Samples/Sample.ValidatingWebApi/Program.cs,Configuration) |
| 35 | + |
| 36 | +#### Custom exception |
| 37 | + |
| 38 | +By default `FluentValidation.ValidationException` exception is raised on the producer and consumer when validation fails. |
| 39 | +It is possible to configure custom exception (or perhaps to suppress the validation errors): |
| 40 | + |
| 41 | +```cs |
| 42 | +builder.Services.AddSlimMessageBus(mbb => |
| 43 | +{ |
| 44 | + mbb.AddFluentValidation(opts => |
| 45 | + { |
| 46 | + // SMB FluentValidation setup goes here |
| 47 | + opts.AddValidationErrorsHandler(errors => new ApplicationException("Custom exception")); |
| 48 | + }); |
| 49 | +}); |
| 50 | +``` |
| 51 | + |
| 52 | +#### Producer side validation |
| 53 | + |
| 54 | +The `.AddProducerValidatorsFromAssemblyContaining()` will register an SMB interceptor that will validate the message upon `.Publish()` or `.Send()` - on the producer side before the message even gets deliverd to the underlying transport. Continuing on the example from previous section: |
| 55 | + |
| 56 | +```cs |
| 57 | +builder.Services.AddSlimMessageBus(mbb => |
| 58 | +{ |
| 59 | + mbb.AddFluentValidation(opts => |
| 60 | + { |
| 61 | + // Register validation interceptors for message (here command) producers inside message bus |
| 62 | + // Required Package: SlimMessageBus.Host.FluentValidation |
| 63 | + opts.AddProducerValidatorsFromAssemblyContaining<CreateCustomerCommandValidator>(); |
| 64 | + }); |
| 65 | +}); |
| 66 | +``` |
| 67 | + |
| 68 | +For example given an ASP.NET Minimal WebApi, the request can be delegated to SlimMessageBus in memory transport: |
| 69 | + |
| 70 | +```cs |
| 71 | +// Using minimal APIs |
| 72 | +var app = builder.Build(); |
| 73 | + |
| 74 | +app.MapPost("/customer", (CreateCustomerCommand command, IMessageBus bus) => bus.Send(command)); |
| 75 | + |
| 76 | +await app.RunAsync(); |
| 77 | +``` |
| 78 | + |
| 79 | +In the situation that the incoming HTTP request where to deliver an invalid command, the request will fail with `FluentValidation.ValidationException: Validation failed` exception. |
| 80 | + |
| 81 | +For full example, please see the [Sample.ValidatingWebApi](../src/Samples/Sample.ValidatingWebApi/) sample. |
| 82 | + |
| 83 | +#### Consumer side validation |
| 84 | + |
| 85 | +We can also enable validation of the incoming message just before it gets delivered to the respective `IConsumer<T>` or `IRequestHandler<T, R>` - on the consumer side. |
| 86 | +Such validation would be needed in scenarios when an external system delivers messages onto the transport (Kafka, Azure Service Bus) which we do not trust, and therefore we could enable validation on the consumer end. This will prevent the invalid messages to enter the consumer or handler. |
| 87 | + |
| 88 | +```cs |
| 89 | +builder.Services.AddSlimMessageBus(mbb => |
| 90 | +{ |
| 91 | + mbb.AddFluentValidation(opts => |
| 92 | + { |
| 93 | + // Register validation interceptors for message (here command) consumers inside message bus |
| 94 | + // Required Package: SlimMessageBus.Host.FluentValidation |
| 95 | + opts.AddConsumerValidatorsFromAssemblyContaining<CreateCustomerCommandValidator>(); |
| 96 | + }); |
| 97 | +}); |
| 98 | +``` |
| 99 | + |
| 100 | +In the situation that the message is invalid, the message will fail with `FluentValidation.ValidationException: Validation failed` exception and standard consumer error handling will take place (depending on the underlying transport it might get retried multiple times until it ends up on dead-letter queue). |
| 101 | + |
| 102 | +### Configuring without MSDI |
| 103 | + |
| 104 | +If you are using another DI container than Microsoft.Extensions.DependencyInjection, in order for the `SlimMessageBus.Host.FluentValidation` plugin to work, you simply need to: |
| 105 | + |
| 106 | +- register the FluentValidator `IValidator<T>` validators in the container, |
| 107 | +- register the respective `ProducerValidationInterceptor<T>` as `IProducerInterceptor<T>` for each of the message type `T` that needs to be validated on producer side, |
| 108 | +- register the respective `ConsumerValidationInterceptor<T>` as `IConsumerInterceptor<T>` for each of the message type `T` that needs to be validated on consumer side, |
| 109 | +- the scope of can be anything that you need (scoped, transient, singleton) |
0 commit comments