From 4a7f8d7ec45c30c810f9a5d95a40cb5a8ea24a2c Mon Sep 17 00:00:00 2001 From: Bart Koelman Date: Tue, 14 Sep 2021 14:44:58 +0200 Subject: [PATCH] Add test for incompatible ID value. By default, this produces: ``` The JSON value could not be converted to JsonApiDotNetCore.Serialization.Objects.SingleOrManyData`1[JsonApiDotNetCore.Serialization.Objects.ResourceObject]. Path: $.data | LineNumber: 3 | BytePositionInLine: 11. ``` which is totally unhelpful. Because this is so likely to hit users, we special-case here to produce a better error. --- .../JsonConverters/ResourceObjectConverter.cs | 8 ++++ .../Updating/Resources/UpdateResourceTests.cs | 40 +++++++++++++++++++ 2 files changed, 48 insertions(+) diff --git a/src/JsonApiDotNetCore/Serialization/JsonConverters/ResourceObjectConverter.cs b/src/JsonApiDotNetCore/Serialization/JsonConverters/ResourceObjectConverter.cs index 8b41a91af2..4414fe1b41 100644 --- a/src/JsonApiDotNetCore/Serialization/JsonConverters/ResourceObjectConverter.cs +++ b/src/JsonApiDotNetCore/Serialization/JsonConverters/ResourceObjectConverter.cs @@ -68,6 +68,14 @@ public override ResourceObject Read(ref Utf8JsonReader reader, Type typeToConver { case "id": { + if (reader.TokenType != JsonTokenType.String) + { + // Newtonsoft.Json used to auto-convert number to strings, while System.Text.Json does not. This is so likely + // to hit users during upgrade that we special-case for this and produce a helpful error message. + var jsonElement = JsonConverterSupport.ReadSubTree(ref reader, options); + throw new JsonException($"Failed to convert ID '{jsonElement}' of type '{jsonElement.ValueKind}' to type 'String'."); + } + resourceObject.Id = reader.GetString(); break; } diff --git a/test/JsonApiDotNetCoreTests/IntegrationTests/ReadWrite/Updating/Resources/UpdateResourceTests.cs b/test/JsonApiDotNetCoreTests/IntegrationTests/ReadWrite/Updating/Resources/UpdateResourceTests.cs index 1b127a18c6..86813f1ec9 100644 --- a/test/JsonApiDotNetCoreTests/IntegrationTests/ReadWrite/Updating/Resources/UpdateResourceTests.cs +++ b/test/JsonApiDotNetCoreTests/IntegrationTests/ReadWrite/Updating/Resources/UpdateResourceTests.cs @@ -958,6 +958,46 @@ await _testContext.RunOnDatabaseAsync(async dbContext => error.Detail.Should().StartWith("Resource ID is read-only. - Request body: <<"); } + [Fact] + public async Task Cannot_update_resource_with_incompatible_ID_value() + { + // Arrange + WorkItem existingWorkItem = _fakers.WorkItem.Generate(); + + await _testContext.RunOnDatabaseAsync(async dbContext => + { + dbContext.WorkItems.Add(existingWorkItem); + await dbContext.SaveChangesAsync(); + }); + + var requestBody = new + { + data = new + { + type = "workItems", + id = existingWorkItem.Id, + attributes = new + { + } + } + }; + + string route = $"/workItems/{existingWorkItem.StringId}"; + + // Act + (HttpResponseMessage httpResponse, Document responseDocument) = await _testContext.ExecutePatchAsync(route, requestBody); + + // Assert + httpResponse.Should().HaveStatusCode(HttpStatusCode.UnprocessableEntity); + + responseDocument.Errors.Should().HaveCount(1); + + ErrorObject error = responseDocument.Errors[0]; + error.StatusCode.Should().Be(HttpStatusCode.UnprocessableEntity); + error.Title.Should().Be("Failed to deserialize request body."); + error.Detail.Should().StartWith("Failed to convert ID '1' of type 'Number' to type 'String'. - Request body: <<"); + } + [Fact] public async Task Cannot_update_resource_with_incompatible_attribute_value() {