Skip to content

Commit 2309534

Browse files
committed
fix: incorrect resource type match
1 parent 88cc6ea commit 2309534

File tree

4 files changed

+121
-10
lines changed

4 files changed

+121
-10
lines changed

src/JsonApiDotNetCore/Middleware/IncomingTypeMatchFilter.cs

Lines changed: 26 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,10 @@
22
using System.Linq;
33
using System.Net.Http;
44
using JsonApiDotNetCore.Configuration;
5+
using JsonApiDotNetCore.Controllers;
56
using JsonApiDotNetCore.Errors;
7+
using JsonApiDotNetCore.Resources;
8+
using Microsoft.AspNetCore.Http;
69
using Microsoft.AspNetCore.Mvc.Filters;
710

811
namespace JsonApiDotNetCore.Middleware
@@ -13,10 +16,12 @@ namespace JsonApiDotNetCore.Middleware
1316
public sealed class IncomingTypeMatchFilter : IActionFilter
1417
{
1518
private readonly IResourceContextProvider _provider;
19+
private readonly IJsonApiRequest _jsonApiRequest;
1620

17-
public IncomingTypeMatchFilter(IResourceContextProvider provider)
21+
public IncomingTypeMatchFilter(IResourceContextProvider provider, IJsonApiRequest jsonApiRequest)
1822
{
1923
_provider = provider;
24+
_jsonApiRequest = jsonApiRequest;
2025
}
2126

2227
public void OnActionExecuting(ActionExecutingContext context)
@@ -27,23 +32,36 @@ public void OnActionExecuting(ActionExecutingContext context)
2732
{
2833
return;
2934
}
30-
35+
3136
var request = context.HttpContext.Request;
32-
if (request.Method == "PATCH" || request.Method == "POST")
37+
38+
if (request.Method == HttpMethods.Patch || request.Method == HttpMethods.Post)
3339
{
34-
var deserializedType = context.ActionArguments.FirstOrDefault().Value?.GetType();
35-
var targetType = context.ActionDescriptor.Parameters.FirstOrDefault()?.ParameterType;
36-
37-
if (deserializedType != null && targetType != null && deserializedType != targetType)
40+
var deserializedType = context.ActionArguments.Last().Value.GetType();
41+
var targetType = (_jsonApiRequest.SecondaryResource ?? _jsonApiRequest.PrimaryResource).ResourceType;
42+
43+
if (deserializedType != targetType)
3844
{
3945
ResourceContext resourceFromEndpoint = _provider.GetResourceContext(targetType);
4046
ResourceContext resourceFromBody = _provider.GetResourceContext(deserializedType);
41-
47+
4248
throw new ResourceTypeMismatchException(new HttpMethod(request.Method), request.Path, resourceFromEndpoint, resourceFromBody);
4349
}
4450
}
4551
}
4652

53+
private void ThrowIfResourceTypeMismatch(Type deserializedType, Type targetType, HttpRequest request)
54+
{
55+
if (deserializedType != targetType)
56+
{
57+
ResourceContext resourceFromEndpoint = _provider.GetResourceContext(targetType);
58+
ResourceContext resourceFromBody = _provider.GetResourceContext(deserializedType);
59+
60+
throw new ResourceTypeMismatchException(new HttpMethod(request.Method), request.Path, resourceFromEndpoint,
61+
resourceFromBody);
62+
}
63+
}
64+
4765
public void OnActionExecuted(ActionExecutedContext context) { /* noop */ }
4866
}
4967
}

test/JsonApiDotNetCoreExampleTests/Acceptance/Spec/CreatingDataTests.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -298,7 +298,7 @@ public async Task CreateResource_ResourceTypeMismatch_IsConflict()
298298
Assert.Equal("Resource type mismatch between request body and endpoint URL.", errorDocument.Errors[0].Title);
299299
Assert.Equal("Expected resource of type 'todoItems' in POST request body at endpoint '/api/v1/todoItems', instead of 'people'.", errorDocument.Errors[0].Detail);
300300
}
301-
301+
302302
[Fact]
303303
public async Task CreateResource_UnknownResourceType_Fails()
304304
{
Lines changed: 93 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,93 @@
1+
using System.Collections.Generic;
2+
using System.Linq;
3+
using System.Net;
4+
using System.Threading.Tasks;
5+
using Bogus;
6+
using JsonApiDotNetCore.Serialization.Objects;
7+
using JsonApiDotNetCoreExample.Models;
8+
using JsonApiDotNetCoreExampleTests.Helpers.Models;
9+
using Microsoft.EntityFrameworkCore;
10+
using Newtonsoft.Json;
11+
using Xunit;
12+
using Person = JsonApiDotNetCoreExample.Models.Person;
13+
14+
namespace JsonApiDotNetCoreExampleTests.Acceptance.Spec
15+
{
16+
public sealed class ResourceTypeMismatchTests : FunctionalTestCollection<StandardApplicationFactory>
17+
{
18+
public ResourceTypeMismatchTests(StandardApplicationFactory factory) : base(factory) { }
19+
20+
[Fact]
21+
public async Task Posting_Resource_With_Mismatching_Resource_Type_Returns_Conflict()
22+
{
23+
// Arrange
24+
string content = JsonConvert.SerializeObject(new
25+
{
26+
data = new
27+
{
28+
type = "people"
29+
}
30+
});
31+
32+
// Act
33+
var (body, response) = await Post("/api/v1/todoItems", content);
34+
35+
// Assert
36+
var errorDocument = JsonConvert.DeserializeObject<ErrorDocument>(body);
37+
Assert.Single(errorDocument.Errors);
38+
Assert.Equal(HttpStatusCode.Conflict, errorDocument.Errors[0].StatusCode);
39+
Assert.Equal("Resource type mismatch between request body and endpoint URL.", errorDocument.Errors[0].Title);
40+
Assert.Equal("Expected resource of type 'todoItems' in POST request body at endpoint '/api/v1/todoItems', instead of 'people'.", errorDocument.Errors[0].Detail);
41+
}
42+
43+
[Fact]
44+
public async Task Patching_Resource_With_Mismatching_Resource_Type_Returns_Conflict()
45+
{
46+
// Arrange
47+
string content = JsonConvert.SerializeObject(new
48+
{
49+
data = new
50+
{
51+
type = "people",
52+
id = 1
53+
}
54+
});
55+
56+
// Act
57+
var (body, response) = await Patch("/api/v1/todoItems/1", content);
58+
59+
// Assert
60+
61+
var errorDocument = JsonConvert.DeserializeObject<ErrorDocument>(body);
62+
Assert.Single(errorDocument.Errors);
63+
Assert.Equal(HttpStatusCode.Conflict, errorDocument.Errors[0].StatusCode);
64+
Assert.Equal("Resource type mismatch between request body and endpoint URL.", errorDocument.Errors[0].Title);
65+
Assert.Equal("Expected resource of type 'todoItems' in PATCH request body at endpoint '/api/v1/todoItems/1', instead of 'people'.", errorDocument.Errors[0].Detail);
66+
}
67+
68+
[Fact]
69+
public async Task Patching_Through_Relationship_Link_With_Mismatching_Resource_Type_Returns_Conflict()
70+
{
71+
// Arrange
72+
string content = JsonConvert.SerializeObject(new
73+
{
74+
data = new
75+
{
76+
type = "todoItems",
77+
id = 1
78+
}
79+
});
80+
81+
// Act
82+
var (body, response) = await Patch("/api/v1/todoItems/1/relationships/owner", content);
83+
84+
// Assert
85+
86+
var errorDocument = JsonConvert.DeserializeObject<ErrorDocument>(body);
87+
Assert.Single(errorDocument.Errors);
88+
Assert.Equal(HttpStatusCode.Conflict, errorDocument.Errors[0].StatusCode);
89+
Assert.Equal("Resource type mismatch between request body and endpoint URL.", errorDocument.Errors[0].Title);
90+
Assert.Equal("Expected resource of type 'people' in PATCH request body at endpoint '/api/v1/todoItems/1/relationships/owner', instead of 'todoItems'.", errorDocument.Errors[0].Detail);
91+
}
92+
}
93+
}

test/JsonApiDotNetCoreExampleTests/Acceptance/Spec/UpdatingRelationshipsTests.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -282,7 +282,7 @@ public async Task Can_Update_ToMany_Relationship_By_Patching_Resource()
282282
// a "complete replace".
283283
Assert.Equal(2, updatedTodoItems.Count);
284284
}
285-
285+
286286
[Fact]
287287
public async Task Can_Update_ToMany_Relationship_By_Patching_Resource_When_Targets_Already_Attached()
288288
{

0 commit comments

Comments
 (0)