TechBuddy.Extensions.Validation 1.1.2

dotnet add package TechBuddy.Extensions.Validation --version 1.1.2
NuGet\Install-Package TechBuddy.Extensions.Validation -Version 1.1.2
This command is intended to be used within the Package Manager Console in Visual Studio, as it uses the NuGet module's version of Install-Package.
<PackageReference Include="TechBuddy.Extensions.Validation" Version="1.1.2" />
For projects that support PackageReference, copy this XML node into the project file to reference the package.
paket add TechBuddy.Extensions.Validation --version 1.1.2
#r "nuget: TechBuddy.Extensions.Validation, 1.1.2"
#r directive can be used in F# Interactive and Polyglot Notebooks. Copy this into the interactive tool or source code of the script to reference the package.
// Install TechBuddy.Extensions.Validation as a Cake Addin
#addin nuget:?package=TechBuddy.Extensions.Validation&version=1.1.2

// Install TechBuddy.Extensions.Validation as a Cake Tool
#tool nuget:?package=TechBuddy.Extensions.Validation&version=1.1.2

Validation Extension

Validation Extension is aiming to give you the opportunity to add FluentValidation in your WebAPI or HttpTrigger Azure Functions. In this library we have extensions methods for two different object which are HttpRequest for Azure Functions and IServiceCollection for WebAPI projects.

<br>

Usages

Test Models Definition
public sealed class TestModel
{
    public int Id { get; set; }

    public string Name { get; set; }
}
public sealed class TestValidator : AbstractValidator<TestModel>
{
    public TestValidator()
    {
        RuleFor(i => i.Id).GreaterThan(0).WithMessage("{PropertyName} cannot be zero!");
        RuleFor(i => i.Name).MinimumLength(3).WithMessage("{PropertyName} must be at least {MinLength} character");
    }
}

<br>

HttpRequest Extensinon Usage

HttpRequest extensinon methods could be used in an Azure Function to validate the HttpRequest.Body by providing a model and IValidator<Model> class that has validation rules.

Usage in Azure Functions 1

In Startup.cs

public override void Configure(IFunctionsHostBuilder builder)
{
    var context = builder.GetContext();

    builder.Services.AddTechBuddyValidator();
}

In Function File

private readonly IValidator<TestModel> testValidator;

public TestFunction(IValidator<TestModel> testValidator)
{
    this.testValidator = testValidator;
}


[FunctionName("Function1")]
public IActionResult Run([HttpTrigger(AuthorizationLevel.Anonymous, "post", Route = null)] TestModel testModel)
{
    var validationResult = testValidator.Validate(testModel); // FluentValidation Validate

    if (!validationResult.IsValid)
        return new BadRequestObjectResult(validationResult.Errors.Select(i => i.ErrorMessage));

    return new OkObjectResult(testModel.Name);
}

<br>

Usage in Azure Functions 2

[FunctionName("Function2")]
    public async Task<IActionResult> RunTest(
        [HttpTrigger(AuthorizationLevel.Anonymous, "post", Route = null)] HttpRequest req)
    {
        var validationResult = await req.ValidateAsync<TestModel, TestValidator>(); // ValidationExtension Validate

        if (!validationResult.IsValid)
            return new BadRequestObjectResult(validationResult.ErrorMessages);

        var testModel = await req.ReadFromJsonAsync<TestModel>();

        return new OkObjectResult(testModel.Name);
    }

<br> <br>

Usage in UnitTests


// Arrange
var model = new TestModel()
{
    Id = 1,
    Name = "Test"
};

var request = HttpRequestBuilder
                .CreateRequest()
                .SetBody(JsonSerializer.Serialize(model))
                .ToRequest();

// Act
var result = await request.ValidateAsync<TestModel, TestValidator>();


// Assert
result.IsValid.Should().BeTrue();
result.Model.Should().BeEquivalentTo(model);
result.ErrorMessages.Should().BeNullOrEmpty();


WebAPI Extensinon Usage

In WebAPI, you can either use HttpRequest Extensions to validate your model manually as we do in Azure Functions, or you can inject all your validators into the system.

There are 3 different extension methods for IServiceCollection, where each has extra ValidationExtensionConfig argument


// It gets the Assembly where TestValidation is in and start scanning for all IValidator<TModel> classes to inject
services.AddTechBuddyValidatorFromAssemblyContaining<TestValidator>();


// It gets the CallingAssembly which is where this method is called from(the WebApi) and start scanning for all IValidator<TModel> classes to inject
services.AddTechBuddyValidator();


// It uses the provided Assembly to start scanning for all IValidator<TModel> classes to inject
var assembly = Assembly.GetExecutingAssembly();
services.AddTechBuddyValidatorFromAssembly(assembly);

Important Config

ValidationExtensionConfig can be provided as a parameter while adding the Validation into our system. That config has UseModelProvider method where you need to pass a class that is derived from IDefaultModelProvider. This model will be used to return and object in ResponseBody once a validation error occured. So basically if there is a validation error, by using IDefaultModelProvider you take the control and decide what you will return in response body. See the example below.

PS: BaseValidationErrorResponseModel is already in the Library, so you don't need to create it.

/// <summary>
/// BaseInvalidResponse Model
/// </summary>
public abstract class BaseValidationErrorResponseModel
{
    /// <summary>
    /// List of exceptions occured
    /// </summary>
    public IEnumerable<string> Errors { get; set; }
}
public class TestResponse : BaseValidationErrorResponseModel
{
    public int HttpStatusCode { get; set; }
}
public class TestModelProvider : IDefaultModelProvider
{
    public object GetModel(ModelStateDictionary.ValueEnumerable modelStateValues)
    {
        return new TestResponse()
        {
            Errors = modelStateValues.SelectMany(i => i.Errors).Select(i => string.Join(Environment.NewLine, i.ErrorMessage)),
            HttpStatusCode = (int)HttpStatusCode.BadRequest
        };
    }
}

In your Startup.cs or Program.cs


builder.Services.AddTechBuddyValidator(config => 
{
    config.UseModelProvider<TestModelProvider>();
});

So, if you configure your app like that, the result will be like below;

Request Body

{
    "id": 0,
    "Name": "Te"
}

Response

{
    "httpStatusCode": 400,
    "errors": [
        "Id cannot be zero!",
        "Name must be at least 3 character"
    ]
}

However, if you used the default(with out ModelProvider) implementation, the response would be like;

{
    "errors": [
        "Id cannot be zero!",
        "Name must be at least 3 character"
    ]
}
Product Compatible and additional computed target framework versions.
.NET net6.0 is compatible.  net6.0-android was computed.  net6.0-ios was computed.  net6.0-maccatalyst was computed.  net6.0-macos was computed.  net6.0-tvos was computed.  net6.0-windows was computed.  net7.0 was computed.  net7.0-android was computed.  net7.0-ios was computed.  net7.0-maccatalyst was computed.  net7.0-macos was computed.  net7.0-tvos was computed.  net7.0-windows was computed.  net8.0 was computed.  net8.0-android was computed.  net8.0-browser was computed.  net8.0-ios was computed.  net8.0-maccatalyst was computed.  net8.0-macos was computed.  net8.0-tvos was computed.  net8.0-windows was computed. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.

NuGet packages

This package is not used by any NuGet packages.

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last updated
1.1.2 338 11/24/2022
1.1.1 304 11/21/2022
1.0.0 307 11/21/2022