Skip to content

Commit

Permalink
json array request body binding for inherited IEnumerable<T> reques…
Browse files Browse the repository at this point in the history
…t DTOs #436
  • Loading branch information
dj-nitehawk committed Apr 21, 2023
1 parent 189ed42 commit d7e082e
Show file tree
Hide file tree
Showing 10 changed files with 276 additions and 8 deletions.
2 changes: 1 addition & 1 deletion Src/Directory.Build.props
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
<Project>
<PropertyGroup>

<Version>5.8.1.14-beta</Version>
<Version>5.8.1.15-beta</Version>

<TargetFrameworks>net6.0;net7.0</TargetFrameworks>
<LangVersion>latest</LangVersion>
Expand Down
8 changes: 4 additions & 4 deletions Src/Library/Binder/RequestBinder.cs
Original file line number Diff line number Diff line change
Expand Up @@ -36,10 +36,10 @@ static RequestBinder()
if (skipModelBinding)
return;

// if the request dto type is an IEnumerable such as List<T>, it will be deserialized by STJ.
// so skip setup for this dto type.
// otherwise, a request dto such as MyRequest<T> can have a value parser, so allow to proceed.
if (tRequest.IsGenericType && tRequest.GetInterfaces().Contains(Types.IEnumerable))
// if the request dto type is an IEnumerable such as List<T>, or any class that implements IEnumerable,
// it will be deserialized by STJ. so skip setup for this dto type.
// otherwise, a request dto such as MyRequest<T> - which is not IEnumerable can have a value parser, so allow to proceed.
if (tRequest.GetInterfaces().Contains(Types.IEnumerable))
return;

foreach (var propInfo in tRequest.BindableProps())
Expand Down
1 change: 1 addition & 0 deletions Src/Library/changelog.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ here's an [example](https://github.com/FastEndpoints/FastEndpoints/blob/4831acea
- `JsonExceptionTransformer` func to enable customization of error messages when STJ throws due to invalid json input [#info](https://discord.com/channels/933662816458645504/1095670893113528370/1095923891605622884)
- `ClearDefaultProduces(200,401,401)` extension method to clear chosen produces metadata added by default #432
- `MarkNonNullablePropsAsRequired()` swagger doc extension for TS client generation with OA3 swagger definitions #388
- json array request body binding for inherited `IEnumerable<T>` request DTOs #436

### IMPROVEMENTS
- add overload to `AddError()`, `ThrowError()`, `ThrowIfAnyErrors()` methods to accept a `ValidationFailure` [#info](https://discord.com/channels/933662816458645504/1090551226598432828/1090934715952926740)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -256,6 +256,25 @@ public async Task JsonArrayBindingToListOfModels()
res?[0].Name.Should().Be("test1");
}

[Fact]
public async Task JsonArrayBindingToIEnumerableDto()
{
var req = new TestCases.JsonArrayBindingToIEnumerableDto.Request()
{
{ new TestCases.JsonArrayBindingToIEnumerableDto.Item() { Id = 1, Name = "one" } },
{ new TestCases.JsonArrayBindingToIEnumerableDto.Item() { Id = 2, Name = "two" } },
};

var (rsp, res) = await GuestClient.POSTAsync<
TestCases.JsonArrayBindingToIEnumerableDto.Endpoint,
TestCases.JsonArrayBindingToIEnumerableDto.Request,
List<TestCases.JsonArrayBindingToIEnumerableDto.Response>>(req);

rsp?.StatusCode.Should().Be(HttpStatusCode.OK);
res?.Count.Should().Be(2);
res.Should().BeEquivalentTo(req);
}

[Fact]
public async Task DupeParamBindingToIEnumerableProps()
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1126,6 +1126,42 @@
}
}
},
"/api/test-cases/json-array-binding-to-ienumerable-dto": {
"post": {
"tags": [
"Test-Cases"
],
"operationId": "TestCasesJsonArrayBindingToIEnumerableDtoEndpoint",
"requestBody": {
"x-name": "Request",
"description": "",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/TestCasesJsonArrayBindingToIEnumerableDtoRequest"
}
}
},
"required": true,
"x-position": 1
},
"responses": {
"200": {
"description": "Success",
"content": {
"application/json": {
"schema": {
"type": "array",
"items": {
"$ref": "#/components/schemas/TestCasesJsonArrayBindingToIEnumerableDtoResponse"
}
}
}
}
}
}
}
},
"/api/test-cases/json-array-binding-for-ienumerable-props": {
"get": {
"tags": [
Expand Down Expand Up @@ -3505,7 +3541,13 @@
},
{
"type": "object",
"additionalProperties": false
"additionalProperties": false,
"properties": {
"Id": {
"type": "integer",
"format": "int32"
}
}
}
]
},
Expand All @@ -3518,6 +3560,36 @@
}
}
},
"TestCasesJsonArrayBindingToIEnumerableDtoResponse": {
"allOf": [
{
"$ref": "#/components/schemas/TestCasesJsonArrayBindingToIEnumerableDtoItem"
},
{
"type": "object",
"additionalProperties": false
}
]
},
"TestCasesJsonArrayBindingToIEnumerableDtoItem": {
"type": "object",
"additionalProperties": false,
"properties": {
"Id": {
"type": "integer",
"format": "int32"
},
"Name": {
"type": "string"
}
}
},
"TestCasesJsonArrayBindingToIEnumerableDtoRequest": {
"type": "array",
"items": {
"$ref": "#/components/schemas/TestCasesJsonArrayBindingToIEnumerableDtoItem"
}
},
"TestCasesJsonArrayBindingForIEnumerablePropsResponse": {
"allOf": [
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1153,6 +1153,42 @@
}
}
},
"/api/test-cases/json-array-binding-to-ienumerable-dto": {
"post": {
"tags": [
"Test-Cases"
],
"operationId": "TestCasesJsonArrayBindingToIEnumerableDtoEndpoint",
"requestBody": {
"x-name": "Request",
"description": "",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/TestCasesJsonArrayBindingToIEnumerableDtoRequest"
}
}
},
"required": true,
"x-position": 1
},
"responses": {
"200": {
"description": "Success",
"content": {
"application/json": {
"schema": {
"type": "array",
"items": {
"$ref": "#/components/schemas/TestCasesJsonArrayBindingToIEnumerableDtoResponse"
}
}
}
}
}
}
}
},
"/api/test-cases/json-array-binding-for-ienumerable-props": {
"get": {
"tags": [
Expand Down Expand Up @@ -3579,7 +3615,13 @@
},
{
"type": "object",
"additionalProperties": false
"additionalProperties": false,
"properties": {
"Id": {
"type": "integer",
"format": "int32"
}
}
}
]
},
Expand All @@ -3592,6 +3634,36 @@
}
}
},
"TestCasesJsonArrayBindingToIEnumerableDtoResponse": {
"allOf": [
{
"$ref": "#/components/schemas/TestCasesJsonArrayBindingToIEnumerableDtoItem"
},
{
"type": "object",
"additionalProperties": false
}
]
},
"TestCasesJsonArrayBindingToIEnumerableDtoItem": {
"type": "object",
"additionalProperties": false,
"properties": {
"Id": {
"type": "integer",
"format": "int32"
},
"Name": {
"type": "string"
}
}
},
"TestCasesJsonArrayBindingToIEnumerableDtoRequest": {
"type": "array",
"items": {
"$ref": "#/components/schemas/TestCasesJsonArrayBindingToIEnumerableDtoItem"
}
},
"TestCasesJsonArrayBindingForIEnumerablePropsResponse": {
"allOf": [
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1126,6 +1126,42 @@
}
}
},
"/api/test-cases/json-array-binding-to-ienumerable-dto": {
"post": {
"tags": [
"Test-Cases"
],
"operationId": "TestCasesJsonArrayBindingToIEnumerableDtoEndpoint",
"requestBody": {
"x-name": "Request",
"description": "",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/TestCasesJsonArrayBindingToIEnumerableDtoRequest"
}
}
},
"required": true,
"x-position": 1
},
"responses": {
"200": {
"description": "Success",
"content": {
"application/json": {
"schema": {
"type": "array",
"items": {
"$ref": "#/components/schemas/TestCasesJsonArrayBindingToIEnumerableDtoResponse"
}
}
}
}
}
}
}
},
"/api/test-cases/json-array-binding-for-ienumerable-props": {
"get": {
"tags": [
Expand Down Expand Up @@ -3373,7 +3409,13 @@
},
{
"type": "object",
"additionalProperties": false
"additionalProperties": false,
"properties": {
"Id": {
"type": "integer",
"format": "int32"
}
}
}
]
},
Expand All @@ -3386,6 +3428,36 @@
}
}
},
"TestCasesJsonArrayBindingToIEnumerableDtoResponse": {
"allOf": [
{
"$ref": "#/components/schemas/TestCasesJsonArrayBindingToIEnumerableDtoItem"
},
{
"type": "object",
"additionalProperties": false
}
]
},
"TestCasesJsonArrayBindingToIEnumerableDtoItem": {
"type": "object",
"additionalProperties": false,
"properties": {
"Id": {
"type": "integer",
"format": "int32"
},
"Name": {
"type": "string"
}
}
},
"TestCasesJsonArrayBindingToIEnumerableDtoRequest": {
"type": "array",
"items": {
"$ref": "#/components/schemas/TestCasesJsonArrayBindingToIEnumerableDtoItem"
}
},
"TestCasesJsonArrayBindingForIEnumerablePropsResponse": {
"allOf": [
{
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
namespace TestCases.JsonArrayBindingToIEnumerableDto;

public class Endpoint : Endpoint<Request, List<Response>>
{
public override void Configure()
{
Post("/test-cases/json-array-binding-to-ienumerable-dto");
AllowAnonymous();
}

public override Task HandleAsync(Request r, CancellationToken c)
{
Response = r.Select(x => new Response { Id = x.Id, Name = x.Name }).ToList();
return Task.CompletedTask;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
namespace TestCases.JsonArrayBindingToIEnumerableDto;

public sealed class Request : List<Item>
{
}

public class Item
{
public int Id { get; set; }
public string Name { get; set; }
}

public class Response : Item
{
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,4 +7,5 @@ public class Request

public class Response : Request
{
public int Id { get; set; }
}

0 comments on commit d7e082e

Please sign in to comment.