Skip to content

Commit 6bf4a8c

Browse files
committed
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
1 parent 6b9d4b8 commit 6bf4a8c

File tree

5 files changed

+39
-38
lines changed

5 files changed

+39
-38
lines changed

Diff for: src/JsonApiDotNetCore/Controllers/BaseJsonApiController.cs

+11-13
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
using System.ComponentModel.DataAnnotations;
21
using JsonApiDotNetCore.Configuration;
32
using JsonApiDotNetCore.Errors;
43
using JsonApiDotNetCore.Middleware;
@@ -107,7 +106,7 @@ public virtual async Task<IActionResult> GetAsync(CancellationToken cancellation
107106
/// GET /articles/1 HTTP/1.1
108107
/// ]]></code>
109108
/// </summary>
110-
public virtual async Task<IActionResult> GetAsync([Required] TId id, CancellationToken cancellationToken)
109+
public virtual async Task<IActionResult> GetAsync(TId id, CancellationToken cancellationToken)
111110
{
112111
_traceWriter.LogMethodStart(new
113112
{
@@ -132,7 +131,7 @@ public virtual async Task<IActionResult> GetAsync([Required] TId id, Cancellatio
132131
/// GET /articles/1/revisions HTTP/1.1
133132
/// ]]></code>
134133
/// </summary>
135-
public virtual async Task<IActionResult> GetSecondaryAsync([Required] TId id, [Required] string relationshipName, CancellationToken cancellationToken)
134+
public virtual async Task<IActionResult> GetSecondaryAsync(TId id, string relationshipName, CancellationToken cancellationToken)
136135
{
137136
_traceWriter.LogMethodStart(new
138137
{
@@ -161,7 +160,7 @@ public virtual async Task<IActionResult> GetSecondaryAsync([Required] TId id, [R
161160
/// GET /articles/1/relationships/revisions HTTP/1.1
162161
/// ]]></code>
163162
/// </summary>
164-
public virtual async Task<IActionResult> GetRelationshipAsync([Required] TId id, [Required] string relationshipName, CancellationToken cancellationToken)
163+
public virtual async Task<IActionResult> GetRelationshipAsync(TId id, string relationshipName, CancellationToken cancellationToken)
165164
{
166165
_traceWriter.LogMethodStart(new
167166
{
@@ -186,7 +185,7 @@ public virtual async Task<IActionResult> GetRelationshipAsync([Required] TId id,
186185
/// POST /articles HTTP/1.1
187186
/// ]]></code>
188187
/// </summary>
189-
public virtual async Task<IActionResult> PostAsync([FromBody] [Required] TResource resource, CancellationToken cancellationToken)
188+
public virtual async Task<IActionResult> PostAsync(TResource resource, CancellationToken cancellationToken)
190189
{
191190
_traceWriter.LogMethodStart(new
192191
{
@@ -236,8 +235,8 @@ public virtual async Task<IActionResult> PostAsync([FromBody] [Required] TResour
236235
/// <param name="cancellationToken">
237236
/// Propagates notification that request handling should be canceled.
238237
/// </param>
239-
public virtual async Task<IActionResult> PostRelationshipAsync([Required] TId id, [Required] string relationshipName,
240-
[FromBody] [Required] ISet<IIdentifiable> rightResourceIds, CancellationToken cancellationToken)
238+
public virtual async Task<IActionResult> PostRelationshipAsync(TId id, string relationshipName, ISet<IIdentifiable> rightResourceIds,
239+
CancellationToken cancellationToken)
241240
{
242241
_traceWriter.LogMethodStart(new
243242
{
@@ -265,7 +264,7 @@ public virtual async Task<IActionResult> PostRelationshipAsync([Required] TId id
265264
/// PATCH /articles/1 HTTP/1.1
266265
/// ]]></code>
267266
/// </summary>
268-
public virtual async Task<IActionResult> PatchAsync([Required] TId id, [FromBody] [Required] TResource resource, CancellationToken cancellationToken)
267+
public virtual async Task<IActionResult> PatchAsync(TId id, TResource resource, CancellationToken cancellationToken)
269268
{
270269
_traceWriter.LogMethodStart(new
271270
{
@@ -311,8 +310,7 @@ public virtual async Task<IActionResult> PatchAsync([Required] TId id, [FromBody
311310
/// <param name="cancellationToken">
312311
/// Propagates notification that request handling should be canceled.
313312
/// </param>
314-
public virtual async Task<IActionResult> PatchRelationshipAsync([Required] TId id, [Required] string relationshipName, [FromBody] object? rightValue,
315-
CancellationToken cancellationToken)
313+
public virtual async Task<IActionResult> PatchRelationshipAsync(TId id, string relationshipName, object? rightValue, CancellationToken cancellationToken)
316314
{
317315
_traceWriter.LogMethodStart(new
318316
{
@@ -338,7 +336,7 @@ public virtual async Task<IActionResult> PatchRelationshipAsync([Required] TId i
338336
/// DELETE /articles/1 HTTP/1.1
339337
/// ]]></code>
340338
/// </summary>
341-
public virtual async Task<IActionResult> DeleteAsync([Required] TId id, CancellationToken cancellationToken)
339+
public virtual async Task<IActionResult> DeleteAsync(TId id, CancellationToken cancellationToken)
342340
{
343341
_traceWriter.LogMethodStart(new
344342
{
@@ -372,8 +370,8 @@ public virtual async Task<IActionResult> DeleteAsync([Required] TId id, Cancella
372370
/// <param name="cancellationToken">
373371
/// Propagates notification that request handling should be canceled.
374372
/// </param>
375-
public virtual async Task<IActionResult> DeleteRelationshipAsync([Required] TId id, [Required] string relationshipName,
376-
[FromBody] [Required] ISet<IIdentifiable> rightResourceIds, CancellationToken cancellationToken)
373+
public virtual async Task<IActionResult> DeleteRelationshipAsync(TId id, string relationshipName, ISet<IIdentifiable> rightResourceIds,
374+
CancellationToken cancellationToken)
377375
{
378376
_traceWriter.LogMethodStart(new
379377
{

Diff for: src/JsonApiDotNetCore/Controllers/BaseJsonApiOperationsController.cs

+1-3
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
using System.ComponentModel.DataAnnotations;
21
using JetBrains.Annotations;
32
using JsonApiDotNetCore.AtomicOperations;
43
using JsonApiDotNetCore.Configuration;
@@ -103,8 +102,7 @@ protected BaseJsonApiOperationsController(IJsonApiOptions options, IResourceGrap
103102
/// }
104103
/// ]]></code>
105104
/// </example>
106-
public virtual async Task<IActionResult> PostOperationsAsync([FromBody] [Required] IList<OperationContainer> operations,
107-
CancellationToken cancellationToken)
105+
public virtual async Task<IActionResult> PostOperationsAsync(IList<OperationContainer> operations, CancellationToken cancellationToken)
108106
{
109107
_traceWriter.LogMethodStart(new
110108
{

Diff for: src/JsonApiDotNetCore/Controllers/JsonApiController.cs

+13-11
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
using System.ComponentModel.DataAnnotations;
12
using JsonApiDotNetCore.Configuration;
23
using JsonApiDotNetCore.Resources;
34
using JsonApiDotNetCore.Services;
@@ -49,68 +50,69 @@ public override Task<IActionResult> GetAsync(CancellationToken cancellationToken
4950
/// <inheritdoc />
5051
[HttpGet("{id}")]
5152
[HttpHead("{id}")]
52-
public override Task<IActionResult> GetAsync(TId id, CancellationToken cancellationToken)
53+
public override Task<IActionResult> GetAsync([Required] TId id, CancellationToken cancellationToken)
5354
{
5455
return base.GetAsync(id, cancellationToken);
5556
}
5657

5758
/// <inheritdoc />
5859
[HttpGet("{id}/{relationshipName}")]
5960
[HttpHead("{id}/{relationshipName}")]
60-
public override Task<IActionResult> GetSecondaryAsync(TId id, string relationshipName, CancellationToken cancellationToken)
61+
public override Task<IActionResult> GetSecondaryAsync([Required] TId id, [Required] string relationshipName, CancellationToken cancellationToken)
6162
{
6263
return base.GetSecondaryAsync(id, relationshipName, cancellationToken);
6364
}
6465

6566
/// <inheritdoc />
6667
[HttpGet("{id}/relationships/{relationshipName}")]
6768
[HttpHead("{id}/relationships/{relationshipName}")]
68-
public override Task<IActionResult> GetRelationshipAsync(TId id, string relationshipName, CancellationToken cancellationToken)
69+
public override Task<IActionResult> GetRelationshipAsync([Required] TId id, [Required] string relationshipName, CancellationToken cancellationToken)
6970
{
7071
return base.GetRelationshipAsync(id, relationshipName, cancellationToken);
7172
}
7273

7374
/// <inheritdoc />
7475
[HttpPost]
75-
public override Task<IActionResult> PostAsync(TResource resource, CancellationToken cancellationToken)
76+
public override Task<IActionResult> PostAsync([FromBody] [Required] TResource resource, CancellationToken cancellationToken)
7677
{
7778
return base.PostAsync(resource, cancellationToken);
7879
}
7980

8081
/// <inheritdoc />
8182
[HttpPost("{id}/relationships/{relationshipName}")]
82-
public override Task<IActionResult> PostRelationshipAsync(TId id, string relationshipName, ISet<IIdentifiable> rightResourceIds,
83-
CancellationToken cancellationToken)
83+
public override Task<IActionResult> PostRelationshipAsync([Required] TId id, [Required] string relationshipName,
84+
[FromBody] [Required] ISet<IIdentifiable> rightResourceIds, CancellationToken cancellationToken)
8485
{
8586
return base.PostRelationshipAsync(id, relationshipName, rightResourceIds, cancellationToken);
8687
}
8788

8889
/// <inheritdoc />
8990
[HttpPatch("{id}")]
90-
public override Task<IActionResult> PatchAsync(TId id, TResource resource, CancellationToken cancellationToken)
91+
public override Task<IActionResult> PatchAsync([Required] TId id, [FromBody] [Required] TResource resource, CancellationToken cancellationToken)
9192
{
9293
return base.PatchAsync(id, resource, cancellationToken);
9394
}
9495

9596
/// <inheritdoc />
9697
[HttpPatch("{id}/relationships/{relationshipName}")]
97-
public override Task<IActionResult> PatchRelationshipAsync(TId id, string relationshipName, [FromBody] object? rightValue,
98+
// Parameter `[Required] object? rightValue` makes Swashbuckle generate the OpenAPI request body as required. We don't actually validate ModelState, so it doesn't hurt.
99+
public override Task<IActionResult> PatchRelationshipAsync([Required] TId id, [Required] string relationshipName, [FromBody] [Required] object? rightValue,
98100
CancellationToken cancellationToken)
99101
{
100102
return base.PatchRelationshipAsync(id, relationshipName, rightValue, cancellationToken);
101103
}
102104

103105
/// <inheritdoc />
104106
[HttpDelete("{id}")]
105-
public override Task<IActionResult> DeleteAsync(TId id, CancellationToken cancellationToken)
107+
public override Task<IActionResult> DeleteAsync([Required] TId id, CancellationToken cancellationToken)
106108
{
107109
return base.DeleteAsync(id, cancellationToken);
108110
}
109111

110112
/// <inheritdoc />
111113
[HttpDelete("{id}/relationships/{relationshipName}")]
112-
public override Task<IActionResult> DeleteRelationshipAsync(TId id, string relationshipName, ISet<IIdentifiable> rightResourceIds,
113-
CancellationToken cancellationToken)
114+
public override Task<IActionResult> DeleteRelationshipAsync([Required] TId id, [Required] string relationshipName,
115+
[FromBody] [Required] ISet<IIdentifiable> rightResourceIds, CancellationToken cancellationToken)
114116
{
115117
return base.DeleteRelationshipAsync(id, relationshipName, rightResourceIds, cancellationToken);
116118
}

Diff for: src/JsonApiDotNetCore/Controllers/JsonApiOperationsController.cs

+2-1
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
using System.ComponentModel.DataAnnotations;
12
using JsonApiDotNetCore.AtomicOperations;
23
using JsonApiDotNetCore.Configuration;
34
using JsonApiDotNetCore.Middleware;
@@ -17,7 +18,7 @@ public abstract class JsonApiOperationsController(
1718
{
1819
/// <inheritdoc />
1920
[HttpPost]
20-
public override Task<IActionResult> PostOperationsAsync(IList<OperationContainer> operations, CancellationToken cancellationToken)
21+
public override Task<IActionResult> PostOperationsAsync([FromBody] [Required] IList<OperationContainer> operations, CancellationToken cancellationToken)
2122
{
2223
return base.PostOperationsAsync(operations, cancellationToken);
2324
}

Diff for: test/JsonApiDotNetCoreTests/IntegrationTests/IdObfuscation/ObfuscatedIdentifiableController.cs

+12-10
Original file line numberDiff line numberDiff line change
@@ -22,21 +22,21 @@ public override Task<IActionResult> GetAsync(CancellationToken cancellationToken
2222
}
2323

2424
[HttpGet("{id}")]
25-
public Task<IActionResult> GetAsync(string id, CancellationToken cancellationToken)
25+
public Task<IActionResult> GetAsync([Required] string id, CancellationToken cancellationToken)
2626
{
2727
int idValue = _codec.Decode(id);
2828
return base.GetAsync(idValue, cancellationToken);
2929
}
3030

3131
[HttpGet("{id}/{relationshipName}")]
32-
public Task<IActionResult> GetSecondaryAsync(string id, string relationshipName, CancellationToken cancellationToken)
32+
public Task<IActionResult> GetSecondaryAsync([Required] string id, [Required] string relationshipName, CancellationToken cancellationToken)
3333
{
3434
int idValue = _codec.Decode(id);
3535
return base.GetSecondaryAsync(idValue, relationshipName, cancellationToken);
3636
}
3737

3838
[HttpGet("{id}/relationships/{relationshipName}")]
39-
public Task<IActionResult> GetRelationshipAsync(string id, string relationshipName, CancellationToken cancellationToken)
39+
public Task<IActionResult> GetRelationshipAsync([Required] string id, [Required] string relationshipName, CancellationToken cancellationToken)
4040
{
4141
int idValue = _codec.Decode(id);
4242
return base.GetRelationshipAsync(idValue, relationshipName, cancellationToken);
@@ -49,37 +49,39 @@ public override Task<IActionResult> PostAsync([FromBody] [Required] TResource re
4949
}
5050

5151
[HttpPost("{id}/relationships/{relationshipName}")]
52-
public Task<IActionResult> PostRelationshipAsync(string id, string relationshipName, [FromBody] [Required] ISet<IIdentifiable> rightResourceIds,
53-
CancellationToken cancellationToken)
52+
public Task<IActionResult> PostRelationshipAsync([Required] string id, [Required] string relationshipName,
53+
[FromBody] [Required] ISet<IIdentifiable> rightResourceIds, CancellationToken cancellationToken)
5454
{
5555
int idValue = _codec.Decode(id);
5656
return base.PostRelationshipAsync(idValue, relationshipName, rightResourceIds, cancellationToken);
5757
}
5858

5959
[HttpPatch("{id}")]
60-
public Task<IActionResult> PatchAsync(string id, [FromBody] [Required] TResource resource, CancellationToken cancellationToken)
60+
public Task<IActionResult> PatchAsync([Required] string id, [FromBody] [Required] TResource resource, CancellationToken cancellationToken)
6161
{
6262
int idValue = _codec.Decode(id);
6363
return base.PatchAsync(idValue, resource, cancellationToken);
6464
}
6565

6666
[HttpPatch("{id}/relationships/{relationshipName}")]
67-
public Task<IActionResult> PatchRelationshipAsync(string id, string relationshipName, [FromBody] object? rightValue, CancellationToken cancellationToken)
67+
// Parameter `[Required] object? rightValue` makes Swashbuckle generate the OpenAPI request body as required. We don't actually validate ModelState, so it doesn't hurt.
68+
public Task<IActionResult> PatchRelationshipAsync([Required] string id, [Required] string relationshipName, [FromBody] [Required] object? rightValue,
69+
CancellationToken cancellationToken)
6870
{
6971
int idValue = _codec.Decode(id);
7072
return base.PatchRelationshipAsync(idValue, relationshipName, rightValue, cancellationToken);
7173
}
7274

7375
[HttpDelete("{id}")]
74-
public Task<IActionResult> DeleteAsync(string id, CancellationToken cancellationToken)
76+
public Task<IActionResult> DeleteAsync([Required] string id, CancellationToken cancellationToken)
7577
{
7678
int idValue = _codec.Decode(id);
7779
return base.DeleteAsync(idValue, cancellationToken);
7880
}
7981

8082
[HttpDelete("{id}/relationships/{relationshipName}")]
81-
public Task<IActionResult> DeleteRelationshipAsync(string id, string relationshipName, [FromBody] [Required] ISet<IIdentifiable> rightResourceIds,
82-
CancellationToken cancellationToken)
83+
public Task<IActionResult> DeleteRelationshipAsync([Required] string id, [Required] string relationshipName,
84+
[FromBody] [Required] ISet<IIdentifiable> rightResourceIds, CancellationToken cancellationToken)
8385
{
8486
int idValue = _codec.Decode(id);
8587
return base.DeleteRelationshipAsync(idValue, relationshipName, rightResourceIds, cancellationToken);

0 commit comments

Comments
 (0)