Fluent Validation in ASP.NET Core 3.1

Full source code available here.

This is an update to a post I wrote in 2017 talking about Fluent Validation in ASP.NET Core.

The example is the same but there has been few updates. The first is how you setup the FluentValidation in Startup.cs, and the second is that you don’t need a ActionFilterAttribute anymore. I have included an example of how to call the action method using Fiddler.

Step 1

Firstly add the package to your Web Api project.
Install-Package FluentValidation.AspNetCore.
If you are going to keep your validators in the Web Api project that is all you need to add.

But if you put the validators in a different project you need to add the FluentValidation package to that project
Install-Package FluentValidation.

Step 2

In startup.cs add –

public void ConfigureServices(IServiceCollection services)
{
     services.AddControllers()
        .AddFluentValidation(fv => fv.RegisterValidatorsFromAssemblyContaining<PersonModelValidator>());}
Step 3

Add a model and a validator.

public class PersonModel
{
    public string FirstName { get; set; }
    public string LastName { get; set; }
}

public class PersonModelValidator : AbstractValidator<PersonModel>
{
    public PersonModelValidator()
    {
        RuleFor(p => p.FirstName).NotEmpty();
        RuleFor(p => p.LastName).Length(5);
    }
}

Example Usage

In Fiddler, Postman or any other tool you POST to localhost:5000/person with the following header –

Content-Type: application/json

And body –

{
    "firstName": "",
    "lastName": "This is too long"
}

Here is how the request looks in Fiddler –

The response will look like this –

{
    "type": "https://tools.ietf.org/html/rfc7231#section-6.5.1",
    "title": "One or more validation errors occurred.",
    "status": 400,
    "traceId": "|84df05e2-41e0d4841bb61293.",
    "errors": {
        "LastName": [
            "'Last Name' must be 5 characters in length. You entered 16 characters."
        ],
        "FirstName": [
            "'First Name' must not be empty."
        ]
    }
}

Easy to read that there are two errors.

Full source code available here.

The terminal shell path “dotnet” is a directory – Visual Studio Code

I have been using Visual Studio Code in Ubuntu Linux for a while, but it was not an easy process to get it working the first time.

I just got around to installing .NET Core 3.1 and hit a familiar problem –

The terminal shell path "dotnet" is a directory

This happens when I try to build inside VS Code using a build task, I point this out because building from a terminal inside VS Code works fine.

Here is the output –

> Executing task: dotnet build /home/bryan/dev/blog/SomeProject/SomeProject.csproj /property:GenerateFullPaths=true /consoleloggerparameters:NoSummary <

The terminal shell path "dotnet" is a directory

Terminal will be reused by tasks, press any key to close it.

If you google this you will find some suggested fixes but they didn’t work for me.

Here is what does work for me –

1. Install the SDKs to a directory NOT named dotnet, my SDK’s are in $HOME/msdotnet
2. Update your PATH to include this directory – PATH=$PATH:$HOME/msdotnet
3. Update your DOTNET_ROOT to this – DOTNET_ROOT=$HOME/msdotnet

That’s what works for me.

Whenever I need to install a new SDK I have do follow the manual steps outlined
here, making sure to unpack the SDK to $HOME/msdotnet.

Hope this works for you, please leave a comment if it does or even if it doesn’t.

Passing Configuration Options Into Middleware, Services and Controllers in ASP.NET Core 3.1

Full source code here.

I recently hit a problem where I needed to reload configuration settings as they changed, fortunately this is relatively straightforward when using the IOptionsMonitor, in .NET Core.

Add a few lines to ConfigureServices, pass the configuration options as a scoped service to the controller or into another service and everything works.

Here is all you need in the ConfigureServices method –

public void ConfigureServices(IServiceCollection services)
{
	services.AddOptions();
	services.Configure<WeatherOptions>(Configuration.GetSection("WeatherOptions"));
	services.AddScoped<ISomeService, SomeService>();

Here’s the service that takes the IOptionsMonitor

public class SomeService : ISomeService
{
	private readonly WeatherOptions _weatherOptions;

	public SomeService(IOptionsMonitor<WeatherOptions> weatherOptions)
	{
		_weatherOptions = weatherOptions.CurrentValue;
	}
// snip..

And here is the controller taking the IOptionsMonitor and the service that also uses the options, this is of course contrived and you almost certainly would not want to take both constructor parameters –

public class WeatherForecastController : ControllerBase
{
	private readonly WeatherOptions _weatherOptions;
	private readonly ISomeService _someService;
	public WeatherForecastController(IOptionsMonitor<WeatherOptions> weatherOptions, ISomeService someService)
	{
		_someService = someService;
		_weatherOptions = weatherOptions.CurrentValue;
	}

But what if you also had to pass those configuration settings into a piece of middleware that executes on each request and if they configuration values change you want to be reflected in the execution of the middleware. This is not so easy because the constructor of a middleware class can only have singletons injected into it, same applies to the Configure method Startup.

Fortunately, the Invoke method can have a scoped service injected into it, see here for more.

public class SomeMiddleware
{
	private readonly RequestDelegate _next;

	public SomeMiddleware(RequestDelegate next)
	{
		_next = next;
	}

	public async Task InvokeAsync(HttpContext context, ISomeService someService)
	{
		someService.DoSomething("Middleware - ");
		await _next(context);
	}
}

To use the middleware, add a single line to the Configure method –

public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
	app.UseMiddleware<SomeMiddleware>();
	//snip..

That’s it.

Full source code here.