diff --git a/docs/guide/durability/marten/event-sourcing.md b/docs/guide/durability/marten/event-sourcing.md index a203c203c..c0eb8166a 100644 --- a/docs/guide/durability/marten/event-sourcing.md +++ b/docs/guide/durability/marten/event-sourcing.md @@ -876,16 +876,23 @@ public class when_transfering_money { // StubEventStream is a type that was recently added to Marten // specifically to facilitate testing logic like this - var fromAccount = new StubEventStream(new Account { Amount = 1000 }){Id = Guid.NewGuid()}; - var toAccount = new StubEventStream(new Account { Amount = 100}){Id = Guid.NewGuid()}); + var fromAccount = new StubEventStream(new Account { Amount = 1000 }) + { + Id = Guid.NewGuid() + }; + + var toAccount = new StubEventStream(new Account { Amount = 100}) + { + Id = Guid.NewGuid() + }; TransferMoneyHandler.Handle(new TransferMoney(fromAccount.Id, toAccount.Id, 100), fromAccount, toAccount); // Now check the events we expected to be appended - fromAccount.Events.Single().ShouldBeOfType().Amount.ShouldBe(100); - toAccount.Events.Single().ShouldBeOfType().Amount.ShouldBe(100); + fromAccount.Events.Single().Data.ShouldBeOfType().Amount.ShouldBe(100); + toAccount.Events.Single().Data.ShouldBeOfType().Amount.ShouldBe(100); } } ``` -snippet source | anchor +snippet source | anchor diff --git a/docs/guide/http/as-parameters.md b/docs/guide/http/as-parameters.md index 0ab4c9694..12dd5e408 100644 --- a/docs/guide/http/as-parameters.md +++ b/docs/guide/http/as-parameters.md @@ -67,7 +67,7 @@ public class AsParametersQuery{ public int? NullableHeader { get; set; } } ``` -snippet source | anchor +snippet source | anchor And the corresponding test case for utilizing this: @@ -110,7 +110,7 @@ response.IntegerNotUsed.ShouldBe(default); response.FloatNotUsed.ShouldBe(default); response.BooleanNotUsed.ShouldBe(default); ``` -snippet source | anchor +snippet source | anchor Wolverine.HTTP is also able to support `[FromServices]`, `[FromBody]`, and `[FromRoute]` bindings as well @@ -152,7 +152,7 @@ public static class AsParametersEndpoints2{ } } ``` -snippet source | anchor +snippet source | anchor And lastly, you can use C# records or really just any constructor function as well @@ -173,5 +173,40 @@ public static class AsParameterRecordEndpoint public static AsParameterRecord Post([AsParameters] AsParameterRecord input) => input; } ``` -snippet source | anchor +snippet source | anchor + + + +The [Fluent Validation middleware](./fluentvalidation) for Wolverine.HTTP is able to validate against request types +bound with `[AsParameters]`: + + + +```cs +public static class ValidatedAsParametersEndpoint +{ + [WolverineGet("/asparameters/validated")] + public static string Get([AsParameters] ValidatedQuery query) + { + return $"{query.Name} is {query.Age}"; + } +} + +public class ValidatedQuery +{ + [FromQuery] + public string? Name { get; set; } + + public int Age { get; set; } + + public class ValidatedQueryValidator : AbstractValidator + { + public ValidatedQueryValidator() + { + RuleFor(x => x.Name).NotNull(); + } + } +} +``` +snippet source | anchor diff --git a/docs/guide/http/fluentvalidation.md b/docs/guide/http/fluentvalidation.md index 128698713..ab714e035 100644 --- a/docs/guide/http/fluentvalidation.md +++ b/docs/guide/http/fluentvalidation.md @@ -46,3 +46,39 @@ app.MapWolverineEndpoints(opts => ``` snippet source | anchor + +## AsParameters Binding + +The Fluent Validation middleware can also be used against the `[AsParameters]` input +of an HTTP endpoint: + + + +```cs +public static class ValidatedAsParametersEndpoint +{ + [WolverineGet("/asparameters/validated")] + public static string Get([AsParameters] ValidatedQuery query) + { + return $"{query.Name} is {query.Age}"; + } +} + +public class ValidatedQuery +{ + [FromQuery] + public string? Name { get; set; } + + public int Age { get; set; } + + public class ValidatedQueryValidator : AbstractValidator + { + public ValidatedQueryValidator() + { + RuleFor(x => x.Name).NotNull(); + } + } +} +``` +snippet source | anchor + diff --git a/docs/guide/http/forms.md b/docs/guide/http/forms.md index 412931056..60a6d6f9e 100644 --- a/docs/guide/http/forms.md +++ b/docs/guide/http/forms.md @@ -82,7 +82,7 @@ You can also use the FromForm attribute on a complex type, Wolverine will then a [WolverinePost("/api/fromformbigquery")] public static BigQuery Post([FromForm] BigQuery query) => query; ``` -snippet source | anchor +snippet source | anchor Individual properties on the class can be aliased using ``[FromForm(Name = "aliased")]`` diff --git a/src/Http/Wolverine.Http.Tests/asparameters_binding.cs b/src/Http/Wolverine.Http.Tests/asparameters_binding.cs index a0b7dc241..d87a3d571 100644 --- a/src/Http/Wolverine.Http.Tests/asparameters_binding.cs +++ b/src/Http/Wolverine.Http.Tests/asparameters_binding.cs @@ -1,3 +1,4 @@ +using Alba; using Microsoft.Extensions.DependencyInjection; using Shouldly; using WolverineWebApi; @@ -10,14 +11,6 @@ public asparameters_binding(AppFixture fixture) : base(fixture) { } - /* - * TODOs - * Bind the body, and when you do that, set the request type - * Bind a service - * Bind a route argument - */ - - [Fact] public async Task fill_all_fields() { @@ -169,4 +162,25 @@ public async Task use_record_for_as_parameters() value.Direction.ShouldBe(Direction.East); value.IsTrue.ShouldBeTrue(); } + + [Fact] + public async Task using_with_FluentValidation_middleware() + { + // Happy path + await Scenario(x => + { + x.Get.Url("/asparameters/validated") + .QueryString("Name", "Jeremy") + .QueryString("Age", "51"); + }); + + var result = await Scenario(x => + { + x.Get.Url("/asparameters/validated") + .QueryString("Age", "51"); + + x.StatusCodeShouldBe(400); + x.ContentTypeShouldBe("application/problem+json"); + }); + } } \ No newline at end of file diff --git a/src/Http/WolverineWebApi/FormEndpoints.cs b/src/Http/WolverineWebApi/FormEndpoints.cs index 3241f47d5..bb3499dbb 100644 --- a/src/Http/WolverineWebApi/FormEndpoints.cs +++ b/src/Http/WolverineWebApi/FormEndpoints.cs @@ -1,4 +1,5 @@ using System.Text.Json.Serialization; +using FluentValidation; using JasperFx.Core; using Marten; using Microsoft.AspNetCore.Mvc; @@ -196,3 +197,33 @@ public static class AsParameterRecordEndpoint #endregion + +#region sample_using_fluent_validation_with_AsParameters + +public static class ValidatedAsParametersEndpoint +{ + [WolverineGet("/asparameters/validated")] + public static string Get([AsParameters] ValidatedQuery query) + { + return $"{query.Name} is {query.Age}"; + } +} + +public class ValidatedQuery +{ + [FromQuery] + public string? Name { get; set; } + + public int Age { get; set; } + + public class ValidatedQueryValidator : AbstractValidator + { + public ValidatedQueryValidator() + { + RuleFor(x => x.Name).NotNull(); + } + } +} + +#endregion +