Skip to content

Instantly share code, notes, and snippets.

@jeroenheijmans
Created August 19, 2019 12:01
Show Gist options
  • Save jeroenheijmans/bfc3d2bc41f23504313bd4da56be127c to your computer and use it in GitHub Desktop.
Save jeroenheijmans/bfc3d2bc41f23504313bd4da56be127c to your computer and use it in GitHub Desktop.
FluentValidation auto-registration with string validators repro
<Project Sdk="Microsoft.NET.Sdk.Web">
<PropertyGroup>
<TargetFramework>netcoreapp2.2</TargetFramework>
<AspNetCoreHostingModel>InProcess</AspNetCoreHostingModel>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="FluentValidation" Version="8.4.0" />
<PackageReference Include="FluentValidation.AspNetCore" Version="8.4.0" />
<PackageReference Include="Microsoft.AspNetCore.App" />
<PackageReference Include="Microsoft.AspNetCore.Razor.Design" Version="2.2.0" PrivateAssets="All" />
</ItemGroup>
</Project>
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netcoreapp2.2</TargetFramework>
<IsPackable>false</IsPackable>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.AspNetCore.App" />
<PackageReference Include="Microsoft.AspNet.WebApi.Client" Version="5.2.7" />
<PackageReference Include="Microsoft.AspNetCore.Mvc.Testing" Version="2.2.0" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.0.1" />
<PackageReference Include="xunit" Version="2.4.0" />
<PackageReference Include="xunit.runner.visualstudio" Version="2.4.0" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\Foo\Foo.csproj" />
</ItemGroup>
</Project>
using Microsoft.AspNetCore;
using Microsoft.AspNetCore.Hosting;
namespace Foo
{
public class Program
{
public static void Main(string[] args)
{
CreateWebHostBuilder(args).Build().Run();
}
public static IWebHostBuilder CreateWebHostBuilder(string[] args) =>
WebHost.CreateDefaultBuilder(args)
.UseStartup<Startup>();
}
}
using FluentValidation.AspNetCore;
using Foo.Controllers;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
namespace Foo
{
public class Startup
{
public Startup(IConfiguration configuration)
{
Configuration = configuration;
}
public IConfiguration Configuration { get; }
public void ConfigureServices(IServiceCollection services)
{
services.AddMvc()
.AddFluentValidation(c => c.RegisterValidatorsFromAssemblyContaining<UserDtoValidator>())
.SetCompatibilityVersion(CompatibilityVersion.Version_2_2);
}
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
app.UseDeveloperExceptionPage();
app.UseHttpsRedirection();
app.UseMvc();
}
}
}
using FluentValidation;
using Microsoft.AspNetCore.Mvc;
namespace Foo.Controllers
{
public class UserDto
{
public string Name { get; set; }
public string Remarks { get; set; }
}
public class SomeOtherValidator : AbstractValidator<string>
{
public SomeOtherValidator()
{
RuleFor(x => x).Must(x => x.StartsWith("bar"));
}
}
public class UserDtoValidator : AbstractValidator<UserDto>
{
public UserDtoValidator()
{
RuleFor(x => x.Name).NotEmpty();
}
}
[Route("api/[controller]")]
[ApiController]
public class UserController : ControllerBase
{
[HttpGet("by-name/{name}")]
public ActionResult<UserDto> GetByName(string name)
{
// TODO: Actual GET logic here
return Ok(new UserDto { Name = name, Remarks = "dummy result" });
}
[HttpGet("by-thing/{thing}")]
public ActionResult<UserDto> GetByThing(string thing)
{
if (!new SomeOtherValidator().Validate(thing).IsValid) return Conflict("Can't start with bar!");
// TODO: Actual GET logic here
return Ok(new UserDto { Name = thing, Remarks = "dummy result" });
}
[HttpPost]
public ActionResult<UserDto> Post([FromBody] UserDto user)
{
// TODO: Actual POST logic here
return Ok(user);
}
}
}
using System.Net;
using System.Net.Http;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc.Testing;
using Xunit;
namespace Foo.Tests
{
public class UserControllerTests : IClassFixture<WebApplicationFactory<Startup>>
{
private readonly WebApplicationFactory<Startup> fixture;
public UserControllerTests(WebApplicationFactory<Startup> fixture)
{
this.fixture = fixture;
}
[Fact]
public async Task Post_can_handle_typical_user()
{
var client = fixture.CreateClient();
var response = await client.PostAsJsonAsync("api/user",
new { Name = "johndoe", Remarks = "test" });
Assert.Equal(HttpStatusCode.OK, response.StatusCode);
}
[Fact]
public async Task Post_requires_username()
{
var client = fixture.CreateClient();
var response = await client.PostAsJsonAsync("api/user", new { Name = "" });
Assert.Equal(HttpStatusCode.BadRequest, response.StatusCode);
}
[Fact]
public async Task Get_by_name_can_return_item()
{
var client = fixture.CreateClient();
var response = await client.GetAsync("api/user/by-name/johndoe");
Assert.Equal(HttpStatusCode.OK, response.StatusCode);
}
[Fact]
public async Task Get_by_thing_conflicts_for_non_bar()
{
var client = fixture.CreateClient();
var response = await client.GetAsync("api/user/by-thing/illegal-arg");
Assert.Equal(HttpStatusCode.Conflict, response.StatusCode);
}
[Fact]
public async Task Get_by_thing_accept_bar_args()
{
var client = fixture.CreateClient();
var response = await client.GetAsync("api/user/by-thing/barthing");
Assert.Equal(HttpStatusCode.OK, response.StatusCode);
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment