Skip to content

Commit 30765c3

Browse files
committed
fix: #520
1 parent d8b4217 commit 30765c3

File tree

5 files changed

+101
-2
lines changed

5 files changed

+101
-2
lines changed

src/JsonApiDotNetCore/Formatters/JsonApiReader.cs

Lines changed: 54 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,9 @@
11
using System;
2+
using System.Collections;
23
using System.IO;
34
using System.Threading.Tasks;
45
using JsonApiDotNetCore.Internal;
6+
using JsonApiDotNetCore.Models;
57
using JsonApiDotNetCore.Serialization;
68
using JsonApiDotNetCore.Services;
79
using Microsoft.AspNetCore.Mvc.Formatters;
@@ -37,7 +39,7 @@ public Task<InputFormatterResult> ReadAsync(InputFormatterContext context)
3739
{
3840
var body = GetRequestBody(context.HttpContext.Request.Body);
3941

40-
object model =null;
42+
object model = null;
4143

4244
if (_jsonApiContext.IsRelationshipPath)
4345
{
@@ -48,10 +50,29 @@ public Task<InputFormatterResult> ReadAsync(InputFormatterContext context)
4850
model = _deSerializer.Deserialize(body);
4951
}
5052

53+
5154
if (model == null)
5255
{
5356
_logger?.LogError("An error occurred while de-serializing the payload");
5457
}
58+
59+
if (context.HttpContext.Request.Method == "PATCH")
60+
{
61+
bool idMissing;
62+
if (model is IList list)
63+
{
64+
idMissing = CheckForId(list);
65+
}
66+
else
67+
{
68+
idMissing = CheckForId(model);
69+
}
70+
if (idMissing)
71+
{
72+
_logger?.LogError("Payload must include id attribute");
73+
throw new JsonApiException(400, "Payload must include id attribute");
74+
}
75+
}
5576
return InputFormatterResult.SuccessAsync(model);
5677
}
5778
catch (Exception ex)
@@ -62,6 +83,38 @@ public Task<InputFormatterResult> ReadAsync(InputFormatterContext context)
6283
}
6384
}
6485

86+
private bool CheckForId(object model)
87+
{
88+
if (model == null) return false;
89+
if (model is ResourceObject ro)
90+
{
91+
if (string.IsNullOrEmpty(ro.Id)) return true;
92+
}
93+
else if (model is IIdentifiable identifiable)
94+
{
95+
if (string.IsNullOrEmpty(identifiable.StringId)) return true;
96+
}
97+
return false;
98+
}
99+
100+
private bool CheckForId(IList modelList)
101+
{
102+
foreach (var model in modelList)
103+
{
104+
if (model == null) continue;
105+
if (model is ResourceObject ro)
106+
{
107+
if (string.IsNullOrEmpty(ro.Id)) return true;
108+
}
109+
else if (model is IIdentifiable identifiable)
110+
{
111+
if (string.IsNullOrEmpty(identifiable.StringId)) return true;
112+
}
113+
}
114+
return false;
115+
116+
}
117+
65118
private string GetRequestBody(Stream body)
66119
{
67120
using (var reader = new StreamReader(body))

test/JsonApiDotNetCoreExampleTests/Acceptance/CamelCasedModelsControllerTests.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -143,6 +143,7 @@ public async Task Can_Patch_CamelCasedModels()
143143
data = new
144144
{
145145
type = "camelCasedModels",
146+
id = model.Id,
146147
attributes = new Dictionary<string, object>()
147148
{
148149
{ "compoundAttr", newModel.CompoundAttr }

test/JsonApiDotNetCoreExampleTests/Acceptance/Spec/UpdatingDataTests.cs

Lines changed: 42 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -55,8 +55,9 @@ public async Task Response400IfUpdatingNotSettableAttribute()
5555

5656
var content = new
5757
{
58-
datea = new
58+
date = new
5959
{
60+
id = todoItem.Id,
6061
type = "todo-items",
6162
attributes = new
6263
{
@@ -90,6 +91,7 @@ public async Task Respond_404_If_EntityDoesNotExist()
9091
{
9192
data = new
9293
{
94+
id = maxPersonId + 100,
9395
type = "todo-items",
9496
attributes = new
9597
{
@@ -108,6 +110,41 @@ public async Task Respond_404_If_EntityDoesNotExist()
108110
Assert.Equal(HttpStatusCode.NotFound, response.StatusCode);
109111
}
110112

113+
[Fact]
114+
public async Task Respond_400_If_IdNotInAttributeList()
115+
{
116+
// Arrange
117+
var maxPersonId = _context.TodoItems.LastOrDefault()?.Id ?? 0;
118+
var todoItem = _todoItemFaker.Generate();
119+
var builder = new WebHostBuilder()
120+
.UseStartup<Startup>();
121+
122+
var server = new TestServer(builder);
123+
var client = server.CreateClient();
124+
125+
var content = new
126+
{
127+
data = new
128+
{
129+
type = "todo-items",
130+
attributes = new
131+
{
132+
description = todoItem.Description,
133+
ordinal = todoItem.Ordinal,
134+
createdDate = DateTime.Now
135+
}
136+
}
137+
};
138+
var request = PrepareRequest("PATCH", $"/api/v1/todo-items/{maxPersonId}", content);
139+
140+
// Act
141+
var response = await client.SendAsync(request);
142+
143+
// Assert
144+
Assert.Equal(422, Convert.ToInt32(response.StatusCode));
145+
146+
}
147+
111148

112149
[Fact]
113150
public async Task Can_Patch_Entity()
@@ -129,6 +166,7 @@ public async Task Can_Patch_Entity()
129166
{
130167
data = new
131168
{
169+
id = todoItem.Id,
132170
type = "todo-items",
133171
attributes = new
134172
{
@@ -187,6 +225,8 @@ public async Task Patch_Entity_With_HasMany_Does_Not_Included_Relationships()
187225
data = new
188226
{
189227
type = "people",
228+
id = person.Id,
229+
190230
attributes = new Dictionary<string, object>
191231
{
192232
{ "last-name", newPerson.LastName },
@@ -234,6 +274,7 @@ public async Task Can_Patch_Entity_And_HasOne_Relationships()
234274
data = new
235275
{
236276
type = "todo-items",
277+
id = todoItem.Id,
237278
attributes = new
238279
{
239280
description = todoItem.Description,

test/JsonApiDotNetCoreExampleTests/Acceptance/Spec/UpdatingRelationshipsTests.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -549,6 +549,7 @@ public async Task Can_Delete_Relationship_By_Patching_Resource()
549549
{
550550
data = new
551551
{
552+
id = todoItem.Id,
552553
type = "todo-items",
553554
relationships = new
554555
{

test/JsonApiDotNetCoreExampleTests/Acceptance/TodoItemsControllerTests.cs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -588,6 +588,7 @@ public async Task Can_Patch_TodoItem()
588588
{
589589
data = new
590590
{
591+
id = todoItem.Id,
591592
type = "todo-items",
592593
attributes = new Dictionary<string, object>()
593594
{
@@ -639,6 +640,7 @@ public async Task Can_Patch_TodoItemWithNullable()
639640
{
640641
data = new
641642
{
643+
id = todoItem.Id,
642644
type = "todo-items",
643645
attributes = new Dictionary<string, object>()
644646
{
@@ -690,6 +692,7 @@ public async Task Can_Patch_TodoItemWithNullValue()
690692
{
691693
data = new
692694
{
695+
id = todoItem.Id,
693696
type = "todo-items",
694697
attributes = new Dictionary<string, object>()
695698
{

0 commit comments

Comments
 (0)