Out-Of-Process Azure Functions with Fluent Validation
Out-Of-Process Azure Functions with Fluent Validation
• Azure Functions, Serverless, .NET, Azure
• 4 min read
Creating Azure Functions has never been easier, thanks to the Out-Of-Process (Isolated) model, which allows us to effortlessly hook services to the middleware. And when it comes to model validation, Fluent Validation comes to the rescue, providing an elegant and efficient way to validate our models.
In this post I’m providing a new implementation of Fluent Validation in Azure Functions with .NET 7, using the Out-Of-Process model. This is a follow-up of my previous article, where I explained how to use Fluent Validation in Azure Functions In-Process with .NET 6.
Creating the project
On Visual Studio 2022 create a new Functions project. Make sure to select the .NET 7 runtime and the Isolated model.
This is the solution was structured with:
Apis: Contains the Azure Functions
Extensions: Contains the Fluent Validation extension
Models: Contains the models and their validators
Requests: Contains the http requests
The NuGet packages FluentValidation and FluentValidation.AspNetCore are assigned to the project.
On Program.cs the Service Collection extension for FluentValidation is hooked up to Service Collection. This is possible because of the NuGet package FluentValidation.AspNetCore.
publicclassProductViewModelValidator:AbstractValidator<ProductViewModel>{publicProductViewModelValidator(){RuleFor(model=>model.Name).NotNull().NotEmpty().WithMessage("Please specify a name");RuleFor(model=>model.Sku).NotNull().NotEmpty().Length(3,10);RuleFor(model=>model.Quantity).GreaterThanOrEqualTo(0);RuleFor(model=>model.Price).NotEqual(0).When(model=>model.Quantity>0).WithMessage("Please specify a price");}}
The rules are simple to understand, for more details please check my previous article.
The Open Api
The solution has a POST endpoint (with OpenApi attributes) that allows users to submit a Product.
The Fluent Validation is executed on the request, and in case it fails, it returns a BadRequest.
publicclassProductApi{privatereadonlyILogger<ProductApi>_logger;privatereadonlyIValidator<ProductViewModel>_validator;publicProductApi(ILogger<ProductApi>log,IValidator<ProductViewModel>validator){_logger=log;_validator=validator;} [Function(nameof(AddProductAsync))] [OpenApiOperation(operationId: nameof(AddProductAsync), tags: new[]{"name"})] [OpenApiSecurity("function_key", SecuritySchemeType.ApiKey, Name = "code", In = OpenApiSecurityLocationType.Query)] [OpenApiRequestBody(contentType: "application/json", bodyType: typeof(ProductViewModel), Description = nameof(ProductViewModel), Required = true)] [OpenApiResponseWithBody(statusCode: HttpStatusCode.OK, contentType: "application/json ", bodyType: typeof(string), Description = "The OK response")]publicasyncTask<HttpResponseData>AddProductAsync([HttpTrigger(AuthorizationLevel.Function,"post")]HttpRequestDatareq){_logger.LogInformation($"{nameof(AddProductAsync)} has been triggered");// Deserialize objectvarproductViewModel=awaitreq.ReadFromJsonAsync<ProductViewModel>();// ValidatingvarproductValidationResult=await_validator.ValidateAsync(productViewModel);if(!productValidationResult.IsValid){varresponseWithErrors=req.CreateResponse();awaitresponseWithErrors.WriteAsJsonAsync(productValidationResult.Errors.Select(e=>new{e.ErrorCode,e.PropertyName,e.ErrorMessage}));responseWithErrors.StatusCode=HttpStatusCode.BadRequest;returnresponseWithErrors;}// TODO: Perform add productvarresponse=req.CreateResponse(HttpStatusCode.OK);returnresponse;}}
NOTE: On the isolated model, there were some changes on how you guarantee the return of the correct HTTP status code. Please check my previous article if you want to know more about that.
Running the solution
Run the solution and open Swagger to perform the POST:
1
http://localhost:7275/api/swagger/ui
That was the port number set for this function.
When submitting a payload that fails the rules, the validation is displayed.
Join the conversation! Share your thoughts and connect with other readers.