diff --git a/JsonApiDotnetCore.sln b/JsonApiDotnetCore.sln
index bb07f87439..06d0d25651 100644
--- a/JsonApiDotnetCore.sln
+++ b/JsonApiDotnetCore.sln
@@ -1,6 +1,7 @@
-Microsoft Visual Studio Solution File, Format Version 12.00
-# Visual Studio 15
-VisualStudioVersion = 15.0.27130.2010
+
+Microsoft Visual Studio Solution File, Format Version 12.00
+# Visual Studio Version 16
+VisualStudioVersion = 16.0.28606.126
MinimumVisualStudioVersion = 10.0.40219.1
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "JsonApiDotNetCore", "src\JsonApiDotNetCore\JsonApiDotNetCore.csproj", "{C0EC9E70-EB2E-436F-9D94-FA16FA774123}"
EndProject
@@ -41,13 +42,13 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "OperationsExample", "src\Ex
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "OperationsExampleTests", "test\OperationsExampleTests\OperationsExampleTests.csproj", "{9CD2C116-D133-4FE4-97DA-A9FEAFF045F1}"
EndProject
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ResourceEntitySeparationExample", "src\Examples\ResourceEntitySeparationExample\ResourceEntitySeparationExample.csproj", "{F4097194-9415-418A-AB4E-315C5D5466AF}"
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ResourceEntitySeparationExample", "src\Examples\ResourceEntitySeparationExample\ResourceEntitySeparationExample.csproj", "{F4097194-9415-418A-AB4E-315C5D5466AF}"
EndProject
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ResourceEntitySeparationExampleTests", "test\ResourceEntitySeparationExampleTests\ResourceEntitySeparationExampleTests.csproj", "{6DFA30D7-1679-4333-9779-6FB678E48EF5}"
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ResourceEntitySeparationExampleTests", "test\ResourceEntitySeparationExampleTests\ResourceEntitySeparationExampleTests.csproj", "{6DFA30D7-1679-4333-9779-6FB678E48EF5}"
EndProject
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "GettingStarted", "src\Examples\GettingStarted\GettingStarted.csproj", "{DF9BFD82-D937-4907-B0B4-64670417115F}"
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "GettingStarted", "src\Examples\GettingStarted\GettingStarted.csproj", "{DF9BFD82-D937-4907-B0B4-64670417115F}"
EndProject
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "DiscoveryTests", "test\DiscoveryTests\DiscoveryTests.csproj", "{09C0C8D8-B721-4955-8889-55CB149C3B5C}"
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "DiscoveryTests", "test\DiscoveryTests\DiscoveryTests.csproj", "{09C0C8D8-B721-4955-8889-55CB149C3B5C}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
@@ -191,6 +192,18 @@ Global
{6DFA30D7-1679-4333-9779-6FB678E48EF5}.Release|x64.Build.0 = Release|Any CPU
{6DFA30D7-1679-4333-9779-6FB678E48EF5}.Release|x86.ActiveCfg = Release|Any CPU
{6DFA30D7-1679-4333-9779-6FB678E48EF5}.Release|x86.Build.0 = Release|Any CPU
+ {DF9BFD82-D937-4907-B0B4-64670417115F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {DF9BFD82-D937-4907-B0B4-64670417115F}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {DF9BFD82-D937-4907-B0B4-64670417115F}.Debug|x64.ActiveCfg = Debug|Any CPU
+ {DF9BFD82-D937-4907-B0B4-64670417115F}.Debug|x64.Build.0 = Debug|Any CPU
+ {DF9BFD82-D937-4907-B0B4-64670417115F}.Debug|x86.ActiveCfg = Debug|Any CPU
+ {DF9BFD82-D937-4907-B0B4-64670417115F}.Debug|x86.Build.0 = Debug|Any CPU
+ {DF9BFD82-D937-4907-B0B4-64670417115F}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {DF9BFD82-D937-4907-B0B4-64670417115F}.Release|Any CPU.Build.0 = Release|Any CPU
+ {DF9BFD82-D937-4907-B0B4-64670417115F}.Release|x64.ActiveCfg = Release|Any CPU
+ {DF9BFD82-D937-4907-B0B4-64670417115F}.Release|x64.Build.0 = Release|Any CPU
+ {DF9BFD82-D937-4907-B0B4-64670417115F}.Release|x86.ActiveCfg = Release|Any CPU
+ {DF9BFD82-D937-4907-B0B4-64670417115F}.Release|x86.Build.0 = Release|Any CPU
{09C0C8D8-B721-4955-8889-55CB149C3B5C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{09C0C8D8-B721-4955-8889-55CB149C3B5C}.Debug|Any CPU.Build.0 = Debug|Any CPU
{09C0C8D8-B721-4955-8889-55CB149C3B5C}.Debug|x64.ActiveCfg = Debug|Any CPU
@@ -203,8 +216,6 @@ Global
{09C0C8D8-B721-4955-8889-55CB149C3B5C}.Release|x64.Build.0 = Release|Any CPU
{09C0C8D8-B721-4955-8889-55CB149C3B5C}.Release|x86.ActiveCfg = Release|Any CPU
{09C0C8D8-B721-4955-8889-55CB149C3B5C}.Release|x86.Build.0 = Release|Any CPU
- {DF9BFD82-D937-4907-B0B4-64670417115F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
- {DF9BFD82-D937-4907-B0B4-64670417115F}.Debug|Any CPU.Build.0 = Debug|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
diff --git a/src/Examples/GettingStarted/GettingStarted.csproj b/src/Examples/GettingStarted/GettingStarted.csproj
index 6e00daefae..ece976d5e5 100644
--- a/src/Examples/GettingStarted/GettingStarted.csproj
+++ b/src/Examples/GettingStarted/GettingStarted.csproj
@@ -5,6 +5,7 @@
+
diff --git a/src/Examples/JsonApiDotNetCoreExample/Models/TodoItem.cs b/src/Examples/JsonApiDotNetCoreExample/Models/TodoItem.cs
index 4683a54b95..b7b84ad780 100644
--- a/src/Examples/JsonApiDotNetCoreExample/Models/TodoItem.cs
+++ b/src/Examples/JsonApiDotNetCoreExample/Models/TodoItem.cs
@@ -29,6 +29,12 @@ public TodoItem()
[Attr("updated-date")]
public DateTime? UpdatedDate { get; set; }
+ [Attr("calculated-value", isImmutable: true)]
+ public string CalculatedValue
+ {
+ get => "joe";
+ }
+
[Attr("offset-date")]
public DateTimeOffset? OffsetDate { get; set; }
diff --git a/src/Examples/JsonApiDotNetCoreExample/Startup.cs b/src/Examples/JsonApiDotNetCoreExample/Startup.cs
index 68ea93a7fc..cd87d804c5 100644
--- a/src/Examples/JsonApiDotNetCoreExample/Startup.cs
+++ b/src/Examples/JsonApiDotNetCoreExample/Startup.cs
@@ -42,7 +42,7 @@ public virtual IServiceProvider ConfigureServices(IServiceCollection services)
},
mvcBuilder,
discovery => discovery.AddCurrentAssembly());
-
+
return services.BuildServiceProvider();
}
diff --git a/src/JsonApiDotNetCore/Formatters/IJsonApiReader.cs b/src/JsonApiDotNetCore/Formatters/IJsonApiReader.cs
index 5b64cc42bf..948a0eac03 100644
--- a/src/JsonApiDotNetCore/Formatters/IJsonApiReader.cs
+++ b/src/JsonApiDotNetCore/Formatters/IJsonApiReader.cs
@@ -3,8 +3,12 @@
namespace JsonApiDotNetCore.Formatters
{
+ ///
+ /// The deserializer of the body, used in .NET core internally
+ /// to process `FromBody`
+ ///
public interface IJsonApiReader
{
Task ReadAsync(InputFormatterContext context);
}
-}
\ No newline at end of file
+}
diff --git a/src/JsonApiDotNetCore/Formatters/JsonApiReader.cs b/src/JsonApiDotNetCore/Formatters/JsonApiReader.cs
index 3acba206fa..b5c39ef4d5 100644
--- a/src/JsonApiDotNetCore/Formatters/JsonApiReader.cs
+++ b/src/JsonApiDotNetCore/Formatters/JsonApiReader.cs
@@ -10,13 +10,13 @@
namespace JsonApiDotNetCore.Formatters
{
+ ///
public class JsonApiReader : IJsonApiReader
{
private readonly IJsonApiDeSerializer _deSerializer;
private readonly IJsonApiContext _jsonApiContext;
private readonly ILogger _logger;
-
public JsonApiReader(IJsonApiDeSerializer deSerializer, IJsonApiContext jsonApiContext, ILoggerFactory loggerFactory)
{
_deSerializer = deSerializer;
@@ -37,13 +37,21 @@ public Task ReadAsync(InputFormatterContext context)
{
var body = GetRequestBody(context.HttpContext.Request.Body);
- var model = _jsonApiContext.IsRelationshipPath ?
- _deSerializer.DeserializeRelationship(body) :
- _deSerializer.Deserialize(body);
+ object model =null;
+
+ if (_jsonApiContext.IsRelationshipPath)
+ {
+ model = _deSerializer.DeserializeRelationship(body);
+ }
+ else
+ {
+ model = _deSerializer.Deserialize(body);
+ }
if (model == null)
+ {
_logger?.LogError("An error occurred while de-serializing the payload");
-
+ }
return InputFormatterResult.SuccessAsync(model);
}
catch (Exception ex)
diff --git a/src/JsonApiDotNetCore/Serialization/JsonApiDeSerializer.cs b/src/JsonApiDotNetCore/Serialization/JsonApiDeSerializer.cs
index 4e3beaaf81..abcae6c9a6 100644
--- a/src/JsonApiDotNetCore/Serialization/JsonApiDeSerializer.cs
+++ b/src/JsonApiDotNetCore/Serialization/JsonApiDeSerializer.cs
@@ -151,11 +151,11 @@ private object SetEntityAttributes(
{
if (attributeValues.TryGetValue(attr.PublicAttributeName, out object newValue))
{
+ if (attr.IsImmutable)
+ continue;
var convertedValue = ConvertAttrValue(newValue, attr.PropertyInfo.PropertyType);
attr.SetValue(entity, convertedValue);
-
- if (attr.IsImmutable == false)
- _jsonApiContext.AttributesToUpdate[attr] = convertedValue;
+ _jsonApiContext.AttributesToUpdate[attr] = convertedValue;
}
}
diff --git a/test/JsonApiDotNetCoreExampleTests/Acceptance/Spec/UpdatingDataTests.cs b/test/JsonApiDotNetCoreExampleTests/Acceptance/Spec/UpdatingDataTests.cs
index 3edb8fa901..5b58a05fe0 100644
--- a/test/JsonApiDotNetCoreExampleTests/Acceptance/Spec/UpdatingDataTests.cs
+++ b/test/JsonApiDotNetCoreExampleTests/Acceptance/Spec/UpdatingDataTests.cs
@@ -1,4 +1,4 @@
-using System;
+using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
@@ -40,10 +40,44 @@ public UpdatingDataTests(TestFixture fixture)
.RuleFor(p => p.LastName, f => f.Name.LastName());
}
+
+ [Fact]
+ public async Task Response400IfUpdatingNotSettableAttribute()
+ {
+ // Arrange
+ var builder = new WebHostBuilder().UseStartup();
+ var server = new TestServer(builder);
+ var client = server.CreateClient();
+
+ var todoItem = _todoItemFaker.Generate();
+ _context.TodoItems.Add(todoItem);
+ _context.SaveChanges();
+
+ var content = new
+ {
+ datea = new
+ {
+ type = "todo-items",
+ attributes = new
+ {
+ calculatedAttribute = "lol"
+ }
+ }
+ };
+ var request = PrepareRequest("PATCH", $"/api/v1/todo-items/{todoItem.Id}", content);
+
+ // Act
+ var response = await client.SendAsync(request);
+
+ // Assert
+ var body = await response.Content.ReadAsStringAsync();
+ Assert.Equal(422, Convert.ToInt32(response.StatusCode));
+ }
+
[Fact]
public async Task Respond_404_If_EntityDoesNotExist()
{
- // arrange
+ // Arrange
var maxPersonId = _context.TodoItems.LastOrDefault()?.Id ?? 0;
var todoItem = _todoItemFaker.Generate();
var builder = new WebHostBuilder()
@@ -65,13 +99,7 @@ public async Task Respond_404_If_EntityDoesNotExist()
}
}
};
-
- var httpMethod = new HttpMethod("PATCH");
- var route = $"/api/v1/todo-items/{maxPersonId + 100}";
- var request = new HttpRequestMessage(httpMethod, route);
-
- request.Content = new StringContent(JsonConvert.SerializeObject(content));
- request.Content.Headers.ContentType = new MediaTypeHeaderValue("application/vnd.api+json");
+ var request = PrepareRequest("PATCH", $"/api/v1/todo-items/{maxPersonId + 100}", content);
// Act
var response = await client.SendAsync(request);
@@ -109,13 +137,7 @@ public async Task Can_Patch_Entity()
}
}
};
-
- var httpMethod = new HttpMethod("PATCH");
- var route = $"/api/v1/todo-items/{todoItem.Id}";
- var request = new HttpRequestMessage(httpMethod, route);
-
- request.Content = new StringContent(JsonConvert.SerializeObject(content));
- request.Content.Headers.ContentType = new MediaTypeHeaderValue("application/vnd.api+json");
+ var request = PrepareRequest("PATCH", $"/api/v1/todo-items/{todoItem.Id}", content);
// Act
var response = await client.SendAsync(request);
@@ -172,13 +194,7 @@ public async Task Patch_Entity_With_HasMany_Does_Not_Included_Relationships()
}
}
};
-
- var httpMethod = new HttpMethod("PATCH");
- var route = $"/api/v1/people/{person.Id}";
- var request = new HttpRequestMessage(httpMethod, route);
-
- request.Content = new StringContent(JsonConvert.SerializeObject(content));
- request.Content.Headers.ContentType = new MediaTypeHeaderValue("application/vnd.api+json");
+ var request = PrepareRequest("PATCH", $"/api/v1/people/{person.Id}", content);
// Act
var response = await client.SendAsync(request);
@@ -237,13 +253,7 @@ public async Task Can_Patch_Entity_And_HasOne_Relationships()
}
}
};
-
- var httpMethod = new HttpMethod("PATCH");
- var route = $"/api/v1/todo-items/{todoItem.Id}";
- var request = new HttpRequestMessage(httpMethod, route);
-
- request.Content = new StringContent(JsonConvert.SerializeObject(content));
- request.Content.Headers.ContentType = new MediaTypeHeaderValue("application/vnd.api+json");
+ var request = PrepareRequest("PATCH", $"/api/v1/todo-items/{todoItem.Id}", content);
// Act
var response = await client.SendAsync(request);
@@ -255,5 +265,15 @@ public async Task Can_Patch_Entity_And_HasOne_Relationships()
Assert.Equal(HttpStatusCode.OK, response.StatusCode);
Assert.Equal(person.Id, updatedTodoItem.OwnerId);
}
+
+ private HttpRequestMessage PrepareRequest(string method, string route, object content)
+ {
+ var httpMethod = new HttpMethod(method);
+ var request = new HttpRequestMessage(httpMethod, route);
+
+ request.Content = new StringContent(JsonConvert.SerializeObject(content));
+ request.Content.Headers.ContentType = new MediaTypeHeaderValue("application/vnd.api+json");
+ return request;
+ }
}
}