From 6bf4a8cd238bfcefb2784bdb720863386c571f5a Mon Sep 17 00:00:00 2001 From: Bart Koelman <10324372+bkoelman@users.noreply.github.com> Date: Sun, 17 Mar 2024 00:40:10 +0100 Subject: [PATCH 1/3] Move back [FromBody] and [Required] to derived controllers, reverting most of the previous PR (#1503). It turns out that ASP.NET ModelState doesn't look at attributes on base parameters --- .../Controllers/BaseJsonApiController.cs | 24 +++++++++---------- .../BaseJsonApiOperationsController.cs | 4 +--- .../Controllers/JsonApiController.cs | 24 ++++++++++--------- .../JsonApiOperationsController.cs | 3 ++- .../ObfuscatedIdentifiableController.cs | 22 +++++++++-------- 5 files changed, 39 insertions(+), 38 deletions(-) diff --git a/src/JsonApiDotNetCore/Controllers/BaseJsonApiController.cs b/src/JsonApiDotNetCore/Controllers/BaseJsonApiController.cs index 1d518c6194..5fee532b36 100644 --- a/src/JsonApiDotNetCore/Controllers/BaseJsonApiController.cs +++ b/src/JsonApiDotNetCore/Controllers/BaseJsonApiController.cs @@ -1,4 +1,3 @@ -using System.ComponentModel.DataAnnotations; using JsonApiDotNetCore.Configuration; using JsonApiDotNetCore.Errors; using JsonApiDotNetCore.Middleware; @@ -107,7 +106,7 @@ public virtual async Task GetAsync(CancellationToken cancellation /// GET /articles/1 HTTP/1.1 /// ]]> /// - public virtual async Task GetAsync([Required] TId id, CancellationToken cancellationToken) + public virtual async Task GetAsync(TId id, CancellationToken cancellationToken) { _traceWriter.LogMethodStart(new { @@ -132,7 +131,7 @@ public virtual async Task GetAsync([Required] TId id, Cancellatio /// GET /articles/1/revisions HTTP/1.1 /// ]]> /// - public virtual async Task GetSecondaryAsync([Required] TId id, [Required] string relationshipName, CancellationToken cancellationToken) + public virtual async Task GetSecondaryAsync(TId id, string relationshipName, CancellationToken cancellationToken) { _traceWriter.LogMethodStart(new { @@ -161,7 +160,7 @@ public virtual async Task GetSecondaryAsync([Required] TId id, [R /// GET /articles/1/relationships/revisions HTTP/1.1 /// ]]> /// - public virtual async Task GetRelationshipAsync([Required] TId id, [Required] string relationshipName, CancellationToken cancellationToken) + public virtual async Task GetRelationshipAsync(TId id, string relationshipName, CancellationToken cancellationToken) { _traceWriter.LogMethodStart(new { @@ -186,7 +185,7 @@ public virtual async Task GetRelationshipAsync([Required] TId id, /// POST /articles HTTP/1.1 /// ]]> /// - public virtual async Task PostAsync([FromBody] [Required] TResource resource, CancellationToken cancellationToken) + public virtual async Task PostAsync(TResource resource, CancellationToken cancellationToken) { _traceWriter.LogMethodStart(new { @@ -236,8 +235,8 @@ public virtual async Task PostAsync([FromBody] [Required] TResour /// /// Propagates notification that request handling should be canceled. /// - public virtual async Task PostRelationshipAsync([Required] TId id, [Required] string relationshipName, - [FromBody] [Required] ISet rightResourceIds, CancellationToken cancellationToken) + public virtual async Task PostRelationshipAsync(TId id, string relationshipName, ISet rightResourceIds, + CancellationToken cancellationToken) { _traceWriter.LogMethodStart(new { @@ -265,7 +264,7 @@ public virtual async Task PostRelationshipAsync([Required] TId id /// PATCH /articles/1 HTTP/1.1 /// ]]> /// - public virtual async Task PatchAsync([Required] TId id, [FromBody] [Required] TResource resource, CancellationToken cancellationToken) + public virtual async Task PatchAsync(TId id, TResource resource, CancellationToken cancellationToken) { _traceWriter.LogMethodStart(new { @@ -311,8 +310,7 @@ public virtual async Task PatchAsync([Required] TId id, [FromBody /// /// Propagates notification that request handling should be canceled. /// - public virtual async Task PatchRelationshipAsync([Required] TId id, [Required] string relationshipName, [FromBody] object? rightValue, - CancellationToken cancellationToken) + public virtual async Task PatchRelationshipAsync(TId id, string relationshipName, object? rightValue, CancellationToken cancellationToken) { _traceWriter.LogMethodStart(new { @@ -338,7 +336,7 @@ public virtual async Task PatchRelationshipAsync([Required] TId i /// DELETE /articles/1 HTTP/1.1 /// ]]> /// - public virtual async Task DeleteAsync([Required] TId id, CancellationToken cancellationToken) + public virtual async Task DeleteAsync(TId id, CancellationToken cancellationToken) { _traceWriter.LogMethodStart(new { @@ -372,8 +370,8 @@ public virtual async Task DeleteAsync([Required] TId id, Cancella /// /// Propagates notification that request handling should be canceled. /// - public virtual async Task DeleteRelationshipAsync([Required] TId id, [Required] string relationshipName, - [FromBody] [Required] ISet rightResourceIds, CancellationToken cancellationToken) + public virtual async Task DeleteRelationshipAsync(TId id, string relationshipName, ISet rightResourceIds, + CancellationToken cancellationToken) { _traceWriter.LogMethodStart(new { diff --git a/src/JsonApiDotNetCore/Controllers/BaseJsonApiOperationsController.cs b/src/JsonApiDotNetCore/Controllers/BaseJsonApiOperationsController.cs index f16a2a6686..1e7da80bd1 100644 --- a/src/JsonApiDotNetCore/Controllers/BaseJsonApiOperationsController.cs +++ b/src/JsonApiDotNetCore/Controllers/BaseJsonApiOperationsController.cs @@ -1,4 +1,3 @@ -using System.ComponentModel.DataAnnotations; using JetBrains.Annotations; using JsonApiDotNetCore.AtomicOperations; using JsonApiDotNetCore.Configuration; @@ -103,8 +102,7 @@ protected BaseJsonApiOperationsController(IJsonApiOptions options, IResourceGrap /// } /// ]]> /// - public virtual async Task PostOperationsAsync([FromBody] [Required] IList operations, - CancellationToken cancellationToken) + public virtual async Task PostOperationsAsync(IList operations, CancellationToken cancellationToken) { _traceWriter.LogMethodStart(new { diff --git a/src/JsonApiDotNetCore/Controllers/JsonApiController.cs b/src/JsonApiDotNetCore/Controllers/JsonApiController.cs index 82719bfeac..0c56500797 100644 --- a/src/JsonApiDotNetCore/Controllers/JsonApiController.cs +++ b/src/JsonApiDotNetCore/Controllers/JsonApiController.cs @@ -1,3 +1,4 @@ +using System.ComponentModel.DataAnnotations; using JsonApiDotNetCore.Configuration; using JsonApiDotNetCore.Resources; using JsonApiDotNetCore.Services; @@ -49,7 +50,7 @@ public override Task GetAsync(CancellationToken cancellationToken /// [HttpGet("{id}")] [HttpHead("{id}")] - public override Task GetAsync(TId id, CancellationToken cancellationToken) + public override Task GetAsync([Required] TId id, CancellationToken cancellationToken) { return base.GetAsync(id, cancellationToken); } @@ -57,7 +58,7 @@ public override Task GetAsync(TId id, CancellationToken cancellat /// [HttpGet("{id}/{relationshipName}")] [HttpHead("{id}/{relationshipName}")] - public override Task GetSecondaryAsync(TId id, string relationshipName, CancellationToken cancellationToken) + public override Task GetSecondaryAsync([Required] TId id, [Required] string relationshipName, CancellationToken cancellationToken) { return base.GetSecondaryAsync(id, relationshipName, cancellationToken); } @@ -65,36 +66,37 @@ public override Task GetSecondaryAsync(TId id, string relationshi /// [HttpGet("{id}/relationships/{relationshipName}")] [HttpHead("{id}/relationships/{relationshipName}")] - public override Task GetRelationshipAsync(TId id, string relationshipName, CancellationToken cancellationToken) + public override Task GetRelationshipAsync([Required] TId id, [Required] string relationshipName, CancellationToken cancellationToken) { return base.GetRelationshipAsync(id, relationshipName, cancellationToken); } /// [HttpPost] - public override Task PostAsync(TResource resource, CancellationToken cancellationToken) + public override Task PostAsync([FromBody] [Required] TResource resource, CancellationToken cancellationToken) { return base.PostAsync(resource, cancellationToken); } /// [HttpPost("{id}/relationships/{relationshipName}")] - public override Task PostRelationshipAsync(TId id, string relationshipName, ISet rightResourceIds, - CancellationToken cancellationToken) + public override Task PostRelationshipAsync([Required] TId id, [Required] string relationshipName, + [FromBody] [Required] ISet rightResourceIds, CancellationToken cancellationToken) { return base.PostRelationshipAsync(id, relationshipName, rightResourceIds, cancellationToken); } /// [HttpPatch("{id}")] - public override Task PatchAsync(TId id, TResource resource, CancellationToken cancellationToken) + public override Task PatchAsync([Required] TId id, [FromBody] [Required] TResource resource, CancellationToken cancellationToken) { return base.PatchAsync(id, resource, cancellationToken); } /// [HttpPatch("{id}/relationships/{relationshipName}")] - public override Task PatchRelationshipAsync(TId id, string relationshipName, [FromBody] object? rightValue, + // Parameter `[Required] object? rightValue` makes Swashbuckle generate the OpenAPI request body as required. We don't actually validate ModelState, so it doesn't hurt. + public override Task PatchRelationshipAsync([Required] TId id, [Required] string relationshipName, [FromBody] [Required] object? rightValue, CancellationToken cancellationToken) { return base.PatchRelationshipAsync(id, relationshipName, rightValue, cancellationToken); @@ -102,15 +104,15 @@ public override Task PatchRelationshipAsync(TId id, string relati /// [HttpDelete("{id}")] - public override Task DeleteAsync(TId id, CancellationToken cancellationToken) + public override Task DeleteAsync([Required] TId id, CancellationToken cancellationToken) { return base.DeleteAsync(id, cancellationToken); } /// [HttpDelete("{id}/relationships/{relationshipName}")] - public override Task DeleteRelationshipAsync(TId id, string relationshipName, ISet rightResourceIds, - CancellationToken cancellationToken) + public override Task DeleteRelationshipAsync([Required] TId id, [Required] string relationshipName, + [FromBody] [Required] ISet rightResourceIds, CancellationToken cancellationToken) { return base.DeleteRelationshipAsync(id, relationshipName, rightResourceIds, cancellationToken); } diff --git a/src/JsonApiDotNetCore/Controllers/JsonApiOperationsController.cs b/src/JsonApiDotNetCore/Controllers/JsonApiOperationsController.cs index bacb5b9342..e2a0417ad2 100644 --- a/src/JsonApiDotNetCore/Controllers/JsonApiOperationsController.cs +++ b/src/JsonApiDotNetCore/Controllers/JsonApiOperationsController.cs @@ -1,3 +1,4 @@ +using System.ComponentModel.DataAnnotations; using JsonApiDotNetCore.AtomicOperations; using JsonApiDotNetCore.Configuration; using JsonApiDotNetCore.Middleware; @@ -17,7 +18,7 @@ public abstract class JsonApiOperationsController( { /// [HttpPost] - public override Task PostOperationsAsync(IList operations, CancellationToken cancellationToken) + public override Task PostOperationsAsync([FromBody] [Required] IList operations, CancellationToken cancellationToken) { return base.PostOperationsAsync(operations, cancellationToken); } diff --git a/test/JsonApiDotNetCoreTests/IntegrationTests/IdObfuscation/ObfuscatedIdentifiableController.cs b/test/JsonApiDotNetCoreTests/IntegrationTests/IdObfuscation/ObfuscatedIdentifiableController.cs index 46391aab7c..1c93a9c08c 100644 --- a/test/JsonApiDotNetCoreTests/IntegrationTests/IdObfuscation/ObfuscatedIdentifiableController.cs +++ b/test/JsonApiDotNetCoreTests/IntegrationTests/IdObfuscation/ObfuscatedIdentifiableController.cs @@ -22,21 +22,21 @@ public override Task GetAsync(CancellationToken cancellationToken } [HttpGet("{id}")] - public Task GetAsync(string id, CancellationToken cancellationToken) + public Task GetAsync([Required] string id, CancellationToken cancellationToken) { int idValue = _codec.Decode(id); return base.GetAsync(idValue, cancellationToken); } [HttpGet("{id}/{relationshipName}")] - public Task GetSecondaryAsync(string id, string relationshipName, CancellationToken cancellationToken) + public Task GetSecondaryAsync([Required] string id, [Required] string relationshipName, CancellationToken cancellationToken) { int idValue = _codec.Decode(id); return base.GetSecondaryAsync(idValue, relationshipName, cancellationToken); } [HttpGet("{id}/relationships/{relationshipName}")] - public Task GetRelationshipAsync(string id, string relationshipName, CancellationToken cancellationToken) + public Task GetRelationshipAsync([Required] string id, [Required] string relationshipName, CancellationToken cancellationToken) { int idValue = _codec.Decode(id); return base.GetRelationshipAsync(idValue, relationshipName, cancellationToken); @@ -49,37 +49,39 @@ public override Task PostAsync([FromBody] [Required] TResource re } [HttpPost("{id}/relationships/{relationshipName}")] - public Task PostRelationshipAsync(string id, string relationshipName, [FromBody] [Required] ISet rightResourceIds, - CancellationToken cancellationToken) + public Task PostRelationshipAsync([Required] string id, [Required] string relationshipName, + [FromBody] [Required] ISet rightResourceIds, CancellationToken cancellationToken) { int idValue = _codec.Decode(id); return base.PostRelationshipAsync(idValue, relationshipName, rightResourceIds, cancellationToken); } [HttpPatch("{id}")] - public Task PatchAsync(string id, [FromBody] [Required] TResource resource, CancellationToken cancellationToken) + public Task PatchAsync([Required] string id, [FromBody] [Required] TResource resource, CancellationToken cancellationToken) { int idValue = _codec.Decode(id); return base.PatchAsync(idValue, resource, cancellationToken); } [HttpPatch("{id}/relationships/{relationshipName}")] - public Task PatchRelationshipAsync(string id, string relationshipName, [FromBody] object? rightValue, CancellationToken cancellationToken) + // Parameter `[Required] object? rightValue` makes Swashbuckle generate the OpenAPI request body as required. We don't actually validate ModelState, so it doesn't hurt. + public Task PatchRelationshipAsync([Required] string id, [Required] string relationshipName, [FromBody] [Required] object? rightValue, + CancellationToken cancellationToken) { int idValue = _codec.Decode(id); return base.PatchRelationshipAsync(idValue, relationshipName, rightValue, cancellationToken); } [HttpDelete("{id}")] - public Task DeleteAsync(string id, CancellationToken cancellationToken) + public Task DeleteAsync([Required] string id, CancellationToken cancellationToken) { int idValue = _codec.Decode(id); return base.DeleteAsync(idValue, cancellationToken); } [HttpDelete("{id}/relationships/{relationshipName}")] - public Task DeleteRelationshipAsync(string id, string relationshipName, [FromBody] [Required] ISet rightResourceIds, - CancellationToken cancellationToken) + public Task DeleteRelationshipAsync([Required] string id, [Required] string relationshipName, + [FromBody] [Required] ISet rightResourceIds, CancellationToken cancellationToken) { int idValue = _codec.Decode(id); return base.DeleteRelationshipAsync(idValue, relationshipName, rightResourceIds, cancellationToken); From aa2c382ce6af8b38b1272ca7d23946ddfb1ff6e7 Mon Sep 17 00:00:00 2001 From: Bart Koelman <10324372+bkoelman@users.noreply.github.com> Date: Sun, 17 Mar 2024 01:26:59 +0100 Subject: [PATCH 2/3] Run tests after merge, which now generates request bodies as required --- .../JsonApiDotNetCoreExample.json | 60 ++++++++++----- .../GeneratedSwagger/swagger.g.json | 9 ++- .../Headers/GeneratedSwagger/swagger.g.json | 15 ++-- .../GeneratedSwagger/swagger.g.json | 75 ++++++++++++------- .../LegacyOpenApiIntegration/swagger.json | 75 ++++++++++++------- .../CamelCase/GeneratedSwagger/swagger.g.json | 27 ++++--- .../KebabCase/GeneratedSwagger/swagger.g.json | 27 ++++--- .../GeneratedSwagger/swagger.g.json | 27 ++++--- .../GeneratedSwagger/swagger.g.json | 36 ++++++--- .../GeneratedSwagger/swagger.g.json | 30 +++++--- .../GeneratedSwagger/swagger.g.json | 30 +++++--- .../GeneratedSwagger/swagger.g.json | 36 ++++++--- .../GeneratedSwagger/swagger.g.json | 36 ++++++--- 13 files changed, 322 insertions(+), 161 deletions(-) diff --git a/src/Examples/JsonApiDotNetCoreExample/GeneratedSwagger/JsonApiDotNetCoreExample.json b/src/Examples/JsonApiDotNetCoreExample/GeneratedSwagger/JsonApiDotNetCoreExample.json index 863e9cd5a9..d9744a9ef0 100644 --- a/src/Examples/JsonApiDotNetCoreExample/GeneratedSwagger/JsonApiDotNetCoreExample.json +++ b/src/Examples/JsonApiDotNetCoreExample/GeneratedSwagger/JsonApiDotNetCoreExample.json @@ -185,7 +185,8 @@ ] } } - } + }, + "required": true }, "responses": { "201": { @@ -469,7 +470,8 @@ ] } } - } + }, + "required": true }, "responses": { "200": { @@ -937,7 +939,8 @@ ] } } - } + }, + "required": true }, "responses": { "204": { @@ -1004,7 +1007,8 @@ ] } } - } + }, + "required": true }, "responses": { "204": { @@ -1071,7 +1075,8 @@ ] } } - } + }, + "required": true }, "responses": { "204": { @@ -1486,7 +1491,8 @@ ] } } - } + }, + "required": true }, "responses": { "204": { @@ -1553,7 +1559,8 @@ ] } } - } + }, + "required": true }, "responses": { "204": { @@ -1620,7 +1627,8 @@ ] } } - } + }, + "required": true }, "responses": { "204": { @@ -1834,7 +1842,8 @@ ] } } - } + }, + "required": true }, "responses": { "201": { @@ -2118,7 +2127,8 @@ ] } } - } + }, + "required": true }, "responses": { "200": { @@ -2586,7 +2596,8 @@ ] } } - } + }, + "required": true }, "responses": { "204": { @@ -2653,7 +2664,8 @@ ] } } - } + }, + "required": true }, "responses": { "204": { @@ -2720,7 +2732,8 @@ ] } } - } + }, + "required": true }, "responses": { "204": { @@ -2934,7 +2947,8 @@ ] } } - } + }, + "required": true }, "responses": { "201": { @@ -3218,7 +3232,8 @@ ] } } - } + }, + "required": true }, "responses": { "200": { @@ -3686,7 +3701,8 @@ ] } } - } + }, + "required": true }, "responses": { "204": { @@ -4101,7 +4117,8 @@ ] } } - } + }, + "required": true }, "responses": { "204": { @@ -4516,7 +4533,8 @@ ] } } - } + }, + "required": true }, "responses": { "204": { @@ -4583,7 +4601,8 @@ ] } } - } + }, + "required": true }, "responses": { "204": { @@ -4650,7 +4669,8 @@ ] } } - } + }, + "required": true }, "responses": { "204": { diff --git a/test/OpenApiTests/ClientIdGenerationModes/GeneratedSwagger/swagger.g.json b/test/OpenApiTests/ClientIdGenerationModes/GeneratedSwagger/swagger.g.json index 115402244f..0761c0b502 100644 --- a/test/OpenApiTests/ClientIdGenerationModes/GeneratedSwagger/swagger.g.json +++ b/test/OpenApiTests/ClientIdGenerationModes/GeneratedSwagger/swagger.g.json @@ -44,7 +44,8 @@ ] } } - } + }, + "required": true }, "responses": { "201": { @@ -137,7 +138,8 @@ ] } } - } + }, + "required": true }, "responses": { "201": { @@ -240,7 +242,8 @@ ] } } - } + }, + "required": true }, "responses": { "201": { diff --git a/test/OpenApiTests/Headers/GeneratedSwagger/swagger.g.json b/test/OpenApiTests/Headers/GeneratedSwagger/swagger.g.json index dc5ad972f3..224a04110b 100644 --- a/test/OpenApiTests/Headers/GeneratedSwagger/swagger.g.json +++ b/test/OpenApiTests/Headers/GeneratedSwagger/swagger.g.json @@ -185,7 +185,8 @@ ] } } - } + }, + "required": true }, "responses": { "201": { @@ -469,7 +470,8 @@ ] } } - } + }, + "required": true }, "responses": { "200": { @@ -937,7 +939,8 @@ ] } } - } + }, + "required": true }, "responses": { "204": { @@ -1004,7 +1007,8 @@ ] } } - } + }, + "required": true }, "responses": { "204": { @@ -1071,7 +1075,8 @@ ] } } - } + }, + "required": true }, "responses": { "204": { diff --git a/test/OpenApiTests/LegacyOpenApiIntegration/GeneratedSwagger/swagger.g.json b/test/OpenApiTests/LegacyOpenApiIntegration/GeneratedSwagger/swagger.g.json index b27522b927..fb074c86b6 100644 --- a/test/OpenApiTests/LegacyOpenApiIntegration/GeneratedSwagger/swagger.g.json +++ b/test/OpenApiTests/LegacyOpenApiIntegration/GeneratedSwagger/swagger.g.json @@ -185,7 +185,8 @@ ] } } - } + }, + "required": true }, "responses": { "201": { @@ -469,7 +470,8 @@ ] } } - } + }, + "required": true }, "responses": { "200": { @@ -937,7 +939,8 @@ ] } } - } + }, + "required": true }, "responses": { "204": { @@ -1004,7 +1007,8 @@ ] } } - } + }, + "required": true }, "responses": { "204": { @@ -1071,7 +1075,8 @@ ] } } - } + }, + "required": true }, "responses": { "204": { @@ -1285,7 +1290,8 @@ ] } } - } + }, + "required": true }, "responses": { "201": { @@ -1569,7 +1575,8 @@ ] } } - } + }, + "required": true }, "responses": { "200": { @@ -2037,7 +2044,8 @@ ] } } - } + }, + "required": true }, "responses": { "204": { @@ -2104,7 +2112,8 @@ ] } } - } + }, + "required": true }, "responses": { "204": { @@ -2171,7 +2180,8 @@ ] } } - } + }, + "required": true }, "responses": { "204": { @@ -2586,7 +2596,8 @@ ] } } - } + }, + "required": true }, "responses": { "204": { @@ -2653,7 +2664,8 @@ ] } } - } + }, + "required": true }, "responses": { "204": { @@ -2720,7 +2732,8 @@ ] } } - } + }, + "required": true }, "responses": { "204": { @@ -2934,7 +2947,8 @@ ] } } - } + }, + "required": true }, "responses": { "201": { @@ -3218,7 +3232,8 @@ ] } } - } + }, + "required": true }, "responses": { "200": { @@ -3686,7 +3701,8 @@ ] } } - } + }, + "required": true }, "responses": { "204": { @@ -4101,7 +4117,8 @@ ] } } - } + }, + "required": true }, "responses": { "204": { @@ -4168,7 +4185,8 @@ ] } } - } + }, + "required": true }, "responses": { "204": { @@ -4235,7 +4253,8 @@ ] } } - } + }, + "required": true }, "responses": { "204": { @@ -4650,7 +4669,8 @@ ] } } - } + }, + "required": true }, "responses": { "204": { @@ -4717,7 +4737,8 @@ ] } } - } + }, + "required": true }, "responses": { "204": { @@ -4784,7 +4805,8 @@ ] } } - } + }, + "required": true }, "responses": { "204": { @@ -5199,7 +5221,8 @@ ] } } - } + }, + "required": true }, "responses": { "204": { @@ -5413,7 +5436,8 @@ ] } } - } + }, + "required": true }, "responses": { "201": { @@ -5697,7 +5721,8 @@ ] } } - } + }, + "required": true }, "responses": { "200": { diff --git a/test/OpenApiTests/LegacyOpenApiIntegration/swagger.json b/test/OpenApiTests/LegacyOpenApiIntegration/swagger.json index b27522b927..fb074c86b6 100644 --- a/test/OpenApiTests/LegacyOpenApiIntegration/swagger.json +++ b/test/OpenApiTests/LegacyOpenApiIntegration/swagger.json @@ -185,7 +185,8 @@ ] } } - } + }, + "required": true }, "responses": { "201": { @@ -469,7 +470,8 @@ ] } } - } + }, + "required": true }, "responses": { "200": { @@ -937,7 +939,8 @@ ] } } - } + }, + "required": true }, "responses": { "204": { @@ -1004,7 +1007,8 @@ ] } } - } + }, + "required": true }, "responses": { "204": { @@ -1071,7 +1075,8 @@ ] } } - } + }, + "required": true }, "responses": { "204": { @@ -1285,7 +1290,8 @@ ] } } - } + }, + "required": true }, "responses": { "201": { @@ -1569,7 +1575,8 @@ ] } } - } + }, + "required": true }, "responses": { "200": { @@ -2037,7 +2044,8 @@ ] } } - } + }, + "required": true }, "responses": { "204": { @@ -2104,7 +2112,8 @@ ] } } - } + }, + "required": true }, "responses": { "204": { @@ -2171,7 +2180,8 @@ ] } } - } + }, + "required": true }, "responses": { "204": { @@ -2586,7 +2596,8 @@ ] } } - } + }, + "required": true }, "responses": { "204": { @@ -2653,7 +2664,8 @@ ] } } - } + }, + "required": true }, "responses": { "204": { @@ -2720,7 +2732,8 @@ ] } } - } + }, + "required": true }, "responses": { "204": { @@ -2934,7 +2947,8 @@ ] } } - } + }, + "required": true }, "responses": { "201": { @@ -3218,7 +3232,8 @@ ] } } - } + }, + "required": true }, "responses": { "200": { @@ -3686,7 +3701,8 @@ ] } } - } + }, + "required": true }, "responses": { "204": { @@ -4101,7 +4117,8 @@ ] } } - } + }, + "required": true }, "responses": { "204": { @@ -4168,7 +4185,8 @@ ] } } - } + }, + "required": true }, "responses": { "204": { @@ -4235,7 +4253,8 @@ ] } } - } + }, + "required": true }, "responses": { "204": { @@ -4650,7 +4669,8 @@ ] } } - } + }, + "required": true }, "responses": { "204": { @@ -4717,7 +4737,8 @@ ] } } - } + }, + "required": true }, "responses": { "204": { @@ -4784,7 +4805,8 @@ ] } } - } + }, + "required": true }, "responses": { "204": { @@ -5199,7 +5221,8 @@ ] } } - } + }, + "required": true }, "responses": { "204": { @@ -5413,7 +5436,8 @@ ] } } - } + }, + "required": true }, "responses": { "201": { @@ -5697,7 +5721,8 @@ ] } } - } + }, + "required": true }, "responses": { "200": { diff --git a/test/OpenApiTests/NamingConventions/CamelCase/GeneratedSwagger/swagger.g.json b/test/OpenApiTests/NamingConventions/CamelCase/GeneratedSwagger/swagger.g.json index d8e8997345..a00ea9ca73 100644 --- a/test/OpenApiTests/NamingConventions/CamelCase/GeneratedSwagger/swagger.g.json +++ b/test/OpenApiTests/NamingConventions/CamelCase/GeneratedSwagger/swagger.g.json @@ -185,7 +185,8 @@ ] } } - } + }, + "required": true }, "responses": { "201": { @@ -469,7 +470,8 @@ ] } } - } + }, + "required": true }, "responses": { "200": { @@ -736,7 +738,8 @@ ] } } - } + }, + "required": true }, "responses": { "201": { @@ -1020,7 +1023,8 @@ ] } } - } + }, + "required": true }, "responses": { "200": { @@ -1488,7 +1492,8 @@ ] } } - } + }, + "required": true }, "responses": { "204": { @@ -1903,7 +1908,8 @@ ] } } - } + }, + "required": true }, "responses": { "204": { @@ -1970,7 +1976,8 @@ ] } } - } + }, + "required": true }, "responses": { "204": { @@ -2037,7 +2044,8 @@ ] } } - } + }, + "required": true }, "responses": { "204": { @@ -2452,7 +2460,8 @@ ] } } - } + }, + "required": true }, "responses": { "204": { diff --git a/test/OpenApiTests/NamingConventions/KebabCase/GeneratedSwagger/swagger.g.json b/test/OpenApiTests/NamingConventions/KebabCase/GeneratedSwagger/swagger.g.json index ae147e39dc..c320df0472 100644 --- a/test/OpenApiTests/NamingConventions/KebabCase/GeneratedSwagger/swagger.g.json +++ b/test/OpenApiTests/NamingConventions/KebabCase/GeneratedSwagger/swagger.g.json @@ -185,7 +185,8 @@ ] } } - } + }, + "required": true }, "responses": { "201": { @@ -469,7 +470,8 @@ ] } } - } + }, + "required": true }, "responses": { "200": { @@ -736,7 +738,8 @@ ] } } - } + }, + "required": true }, "responses": { "201": { @@ -1020,7 +1023,8 @@ ] } } - } + }, + "required": true }, "responses": { "200": { @@ -1488,7 +1492,8 @@ ] } } - } + }, + "required": true }, "responses": { "204": { @@ -1903,7 +1908,8 @@ ] } } - } + }, + "required": true }, "responses": { "204": { @@ -1970,7 +1976,8 @@ ] } } - } + }, + "required": true }, "responses": { "204": { @@ -2037,7 +2044,8 @@ ] } } - } + }, + "required": true }, "responses": { "204": { @@ -2452,7 +2460,8 @@ ] } } - } + }, + "required": true }, "responses": { "204": { diff --git a/test/OpenApiTests/NamingConventions/PascalCase/GeneratedSwagger/swagger.g.json b/test/OpenApiTests/NamingConventions/PascalCase/GeneratedSwagger/swagger.g.json index fe05f25095..e14fb2afb7 100644 --- a/test/OpenApiTests/NamingConventions/PascalCase/GeneratedSwagger/swagger.g.json +++ b/test/OpenApiTests/NamingConventions/PascalCase/GeneratedSwagger/swagger.g.json @@ -185,7 +185,8 @@ ] } } - } + }, + "required": true }, "responses": { "201": { @@ -469,7 +470,8 @@ ] } } - } + }, + "required": true }, "responses": { "200": { @@ -736,7 +738,8 @@ ] } } - } + }, + "required": true }, "responses": { "201": { @@ -1020,7 +1023,8 @@ ] } } - } + }, + "required": true }, "responses": { "200": { @@ -1488,7 +1492,8 @@ ] } } - } + }, + "required": true }, "responses": { "204": { @@ -1903,7 +1908,8 @@ ] } } - } + }, + "required": true }, "responses": { "204": { @@ -1970,7 +1976,8 @@ ] } } - } + }, + "required": true }, "responses": { "204": { @@ -2037,7 +2044,8 @@ ] } } - } + }, + "required": true }, "responses": { "204": { @@ -2452,7 +2460,8 @@ ] } } - } + }, + "required": true }, "responses": { "204": { diff --git a/test/OpenApiTests/QueryStrings/GeneratedSwagger/swagger.g.json b/test/OpenApiTests/QueryStrings/GeneratedSwagger/swagger.g.json index a3118dc50d..68a284eaeb 100644 --- a/test/OpenApiTests/QueryStrings/GeneratedSwagger/swagger.g.json +++ b/test/OpenApiTests/QueryStrings/GeneratedSwagger/swagger.g.json @@ -185,7 +185,8 @@ ] } } - } + }, + "required": true }, "responses": { "201": { @@ -469,7 +470,8 @@ ] } } - } + }, + "required": true }, "responses": { "200": { @@ -937,7 +939,8 @@ ] } } - } + }, + "required": true }, "responses": { "204": { @@ -1151,7 +1154,8 @@ ] } } - } + }, + "required": true }, "responses": { "201": { @@ -1435,7 +1439,8 @@ ] } } - } + }, + "required": true }, "responses": { "200": { @@ -1903,7 +1908,8 @@ ] } } - } + }, + "required": true }, "responses": { "204": { @@ -1970,7 +1976,8 @@ ] } } - } + }, + "required": true }, "responses": { "204": { @@ -2037,7 +2044,8 @@ ] } } - } + }, + "required": true }, "responses": { "204": { @@ -2452,7 +2460,8 @@ ] } } - } + }, + "required": true }, "responses": { "204": { @@ -2867,7 +2876,8 @@ ] } } - } + }, + "required": true }, "responses": { "204": { @@ -2934,7 +2944,8 @@ ] } } - } + }, + "required": true }, "responses": { "204": { @@ -3001,7 +3012,8 @@ ] } } - } + }, + "required": true }, "responses": { "204": { diff --git a/test/OpenApiTests/ResourceFieldValidation/NullableReferenceTypesOff/ModelStateValidationOff/GeneratedSwagger/swagger.g.json b/test/OpenApiTests/ResourceFieldValidation/NullableReferenceTypesOff/ModelStateValidationOff/GeneratedSwagger/swagger.g.json index 9b3dda8aac..80872daa7a 100644 --- a/test/OpenApiTests/ResourceFieldValidation/NullableReferenceTypesOff/ModelStateValidationOff/GeneratedSwagger/swagger.g.json +++ b/test/OpenApiTests/ResourceFieldValidation/NullableReferenceTypesOff/ModelStateValidationOff/GeneratedSwagger/swagger.g.json @@ -185,7 +185,8 @@ ] } } - } + }, + "required": true }, "responses": { "201": { @@ -469,7 +470,8 @@ ] } } - } + }, + "required": true }, "responses": { "200": { @@ -937,7 +939,8 @@ ] } } - } + }, + "required": true }, "responses": { "204": { @@ -1004,7 +1007,8 @@ ] } } - } + }, + "required": true }, "responses": { "204": { @@ -1071,7 +1075,8 @@ ] } } - } + }, + "required": true }, "responses": { "204": { @@ -1486,7 +1491,8 @@ ] } } - } + }, + "required": true }, "responses": { "204": { @@ -1901,7 +1907,8 @@ ] } } - } + }, + "required": true }, "responses": { "204": { @@ -1968,7 +1975,8 @@ ] } } - } + }, + "required": true }, "responses": { "204": { @@ -2035,7 +2043,8 @@ ] } } - } + }, + "required": true }, "responses": { "204": { @@ -2450,7 +2459,8 @@ ] } } - } + }, + "required": true }, "responses": { "204": { diff --git a/test/OpenApiTests/ResourceFieldValidation/NullableReferenceTypesOff/ModelStateValidationOn/GeneratedSwagger/swagger.g.json b/test/OpenApiTests/ResourceFieldValidation/NullableReferenceTypesOff/ModelStateValidationOn/GeneratedSwagger/swagger.g.json index 91a4259052..e4ca77d48e 100644 --- a/test/OpenApiTests/ResourceFieldValidation/NullableReferenceTypesOff/ModelStateValidationOn/GeneratedSwagger/swagger.g.json +++ b/test/OpenApiTests/ResourceFieldValidation/NullableReferenceTypesOff/ModelStateValidationOn/GeneratedSwagger/swagger.g.json @@ -185,7 +185,8 @@ ] } } - } + }, + "required": true }, "responses": { "201": { @@ -469,7 +470,8 @@ ] } } - } + }, + "required": true }, "responses": { "200": { @@ -937,7 +939,8 @@ ] } } - } + }, + "required": true }, "responses": { "204": { @@ -1004,7 +1007,8 @@ ] } } - } + }, + "required": true }, "responses": { "204": { @@ -1071,7 +1075,8 @@ ] } } - } + }, + "required": true }, "responses": { "204": { @@ -1486,7 +1491,8 @@ ] } } - } + }, + "required": true }, "responses": { "204": { @@ -1901,7 +1907,8 @@ ] } } - } + }, + "required": true }, "responses": { "204": { @@ -1968,7 +1975,8 @@ ] } } - } + }, + "required": true }, "responses": { "204": { @@ -2035,7 +2043,8 @@ ] } } - } + }, + "required": true }, "responses": { "204": { @@ -2450,7 +2459,8 @@ ] } } - } + }, + "required": true }, "responses": { "204": { diff --git a/test/OpenApiTests/ResourceFieldValidation/NullableReferenceTypesOn/ModelStateValidationOff/GeneratedSwagger/swagger.g.json b/test/OpenApiTests/ResourceFieldValidation/NullableReferenceTypesOn/ModelStateValidationOff/GeneratedSwagger/swagger.g.json index 832c851f75..5cc11103e5 100644 --- a/test/OpenApiTests/ResourceFieldValidation/NullableReferenceTypesOn/ModelStateValidationOff/GeneratedSwagger/swagger.g.json +++ b/test/OpenApiTests/ResourceFieldValidation/NullableReferenceTypesOn/ModelStateValidationOff/GeneratedSwagger/swagger.g.json @@ -185,7 +185,8 @@ ] } } - } + }, + "required": true }, "responses": { "201": { @@ -469,7 +470,8 @@ ] } } - } + }, + "required": true }, "responses": { "200": { @@ -937,7 +939,8 @@ ] } } - } + }, + "required": true }, "responses": { "204": { @@ -1352,7 +1355,8 @@ ] } } - } + }, + "required": true }, "responses": { "204": { @@ -1767,7 +1771,8 @@ ] } } - } + }, + "required": true }, "responses": { "204": { @@ -2182,7 +2187,8 @@ ] } } - } + }, + "required": true }, "responses": { "204": { @@ -2597,7 +2603,8 @@ ] } } - } + }, + "required": true }, "responses": { "204": { @@ -2664,7 +2671,8 @@ ] } } - } + }, + "required": true }, "responses": { "204": { @@ -2731,7 +2739,8 @@ ] } } - } + }, + "required": true }, "responses": { "204": { @@ -3146,7 +3155,8 @@ ] } } - } + }, + "required": true }, "responses": { "204": { @@ -3213,7 +3223,8 @@ ] } } - } + }, + "required": true }, "responses": { "204": { @@ -3280,7 +3291,8 @@ ] } } - } + }, + "required": true }, "responses": { "204": { diff --git a/test/OpenApiTests/ResourceFieldValidation/NullableReferenceTypesOn/ModelStateValidationOn/GeneratedSwagger/swagger.g.json b/test/OpenApiTests/ResourceFieldValidation/NullableReferenceTypesOn/ModelStateValidationOn/GeneratedSwagger/swagger.g.json index b453d1536b..ffe5d36cb9 100644 --- a/test/OpenApiTests/ResourceFieldValidation/NullableReferenceTypesOn/ModelStateValidationOn/GeneratedSwagger/swagger.g.json +++ b/test/OpenApiTests/ResourceFieldValidation/NullableReferenceTypesOn/ModelStateValidationOn/GeneratedSwagger/swagger.g.json @@ -185,7 +185,8 @@ ] } } - } + }, + "required": true }, "responses": { "201": { @@ -469,7 +470,8 @@ ] } } - } + }, + "required": true }, "responses": { "200": { @@ -937,7 +939,8 @@ ] } } - } + }, + "required": true }, "responses": { "204": { @@ -1352,7 +1355,8 @@ ] } } - } + }, + "required": true }, "responses": { "204": { @@ -1767,7 +1771,8 @@ ] } } - } + }, + "required": true }, "responses": { "204": { @@ -2182,7 +2187,8 @@ ] } } - } + }, + "required": true }, "responses": { "204": { @@ -2597,7 +2603,8 @@ ] } } - } + }, + "required": true }, "responses": { "204": { @@ -2664,7 +2671,8 @@ ] } } - } + }, + "required": true }, "responses": { "204": { @@ -2731,7 +2739,8 @@ ] } } - } + }, + "required": true }, "responses": { "204": { @@ -3146,7 +3155,8 @@ ] } } - } + }, + "required": true }, "responses": { "204": { @@ -3213,7 +3223,8 @@ ] } } - } + }, + "required": true }, "responses": { "204": { @@ -3280,7 +3291,8 @@ ] } } - } + }, + "required": true }, "responses": { "204": { From 41524d4f5ae1a569fd3802e6561e6de237224fee Mon Sep 17 00:00:00 2001 From: Bart Koelman <10324372+bkoelman@users.noreply.github.com> Date: Sun, 17 Mar 2024 01:36:30 +0100 Subject: [PATCH 3/3] Use newer GetCustomAttributes method --- .../JsonApiMetadata/EndpointResolver.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/JsonApiDotNetCore.OpenApi/JsonApiMetadata/EndpointResolver.cs b/src/JsonApiDotNetCore.OpenApi/JsonApiMetadata/EndpointResolver.cs index c7a17242ea..fe4f9153d8 100644 --- a/src/JsonApiDotNetCore.OpenApi/JsonApiMetadata/EndpointResolver.cs +++ b/src/JsonApiDotNetCore.OpenApi/JsonApiMetadata/EndpointResolver.cs @@ -16,7 +16,7 @@ internal sealed class EndpointResolver return null; } - HttpMethodAttribute? method = controllerAction.GetCustomAttributes(true).OfType().FirstOrDefault(); + HttpMethodAttribute? method = Attribute.GetCustomAttributes(controllerAction, true).OfType().FirstOrDefault(); return ResolveJsonApiEndpoint(method); }