From 8cb0fb80c44cc584e02a64beee0e6744bd9d98ea Mon Sep 17 00:00:00 2001 From: Bart Koelman Date: Wed, 13 May 2020 18:11:36 +0200 Subject: [PATCH 1/2] Show helpful error instead of crash when ActionResult returns unsupported type. --- .../Controllers/TodoItemsTestController.cs | 4 ++- .../Client/IRequestSerializer.cs | 2 +- .../Serialization/Client/RequestSerializer.cs | 2 +- .../Serialization/Common/DocumentBuilder.cs | 2 +- .../Server/ResponseSerializer.cs | 22 +++++++++--- .../Acceptance/ActionResultTests.cs | 36 +++++++++++++++++++ .../Serialization/SerializerTestsSetup.cs | 2 +- 7 files changed, 60 insertions(+), 10 deletions(-) diff --git a/src/Examples/JsonApiDotNetCoreExample/Controllers/TodoItemsTestController.cs b/src/Examples/JsonApiDotNetCoreExample/Controllers/TodoItemsTestController.cs index f7085a1890..02b0585893 100644 --- a/src/Examples/JsonApiDotNetCoreExample/Controllers/TodoItemsTestController.cs +++ b/src/Examples/JsonApiDotNetCoreExample/Controllers/TodoItemsTestController.cs @@ -61,7 +61,9 @@ public override async Task PostAsync(TodoItem entity) [HttpPatch("{id}")] public override async Task PatchAsync(int id, [FromBody] TodoItem entity) { - return await base.PatchAsync(id, entity); + await Task.Yield(); + + return Conflict("Something went wrong"); } [HttpPatch("{id}/relationships/{relationshipName}")] diff --git a/src/JsonApiDotNetCore/Serialization/Client/IRequestSerializer.cs b/src/JsonApiDotNetCore/Serialization/Client/IRequestSerializer.cs index f90e7c041a..244b30f70e 100644 --- a/src/JsonApiDotNetCore/Serialization/Client/IRequestSerializer.cs +++ b/src/JsonApiDotNetCore/Serialization/Client/IRequestSerializer.cs @@ -22,7 +22,7 @@ public interface IRequestSerializer /// /// Entities to serialize /// The serialized content - string Serialize(IEnumerable entities); + string Serialize(IEnumerable entities); /// /// Sets the attributes that will be included in the serialized payload. /// You can use diff --git a/src/JsonApiDotNetCore/Serialization/Client/RequestSerializer.cs b/src/JsonApiDotNetCore/Serialization/Client/RequestSerializer.cs index 2dcc449482..fff4639d73 100644 --- a/src/JsonApiDotNetCore/Serialization/Client/RequestSerializer.cs +++ b/src/JsonApiDotNetCore/Serialization/Client/RequestSerializer.cs @@ -41,7 +41,7 @@ public string Serialize(IIdentifiable entity) } /// - public string Serialize(IEnumerable entities) + public string Serialize(IEnumerable entities) { IIdentifiable entity = null; foreach (IIdentifiable item in entities) diff --git a/src/JsonApiDotNetCore/Serialization/Common/DocumentBuilder.cs b/src/JsonApiDotNetCore/Serialization/Common/DocumentBuilder.cs index a154a6ae23..896fadec3b 100644 --- a/src/JsonApiDotNetCore/Serialization/Common/DocumentBuilder.cs +++ b/src/JsonApiDotNetCore/Serialization/Common/DocumentBuilder.cs @@ -44,7 +44,7 @@ protected Document Build(IIdentifiable entity, IReadOnlyCollectionAttributes to include in the building process /// Relationships to include in the building process /// The resource object that was built - protected Document Build(IEnumerable entities, IReadOnlyCollection attributes, IReadOnlyCollection relationships) + protected Document Build(IEnumerable entities, IReadOnlyCollection attributes, IReadOnlyCollection relationships) { var data = new List(); foreach (IIdentifiable entity in entities) diff --git a/src/JsonApiDotNetCore/Serialization/Server/ResponseSerializer.cs b/src/JsonApiDotNetCore/Serialization/Server/ResponseSerializer.cs index 720f782f49..0b74f0863d 100644 --- a/src/JsonApiDotNetCore/Serialization/Server/ResponseSerializer.cs +++ b/src/JsonApiDotNetCore/Serialization/Server/ResponseSerializer.cs @@ -3,6 +3,7 @@ using System.Collections.Generic; using JsonApiDotNetCore.Configuration; using JsonApiDotNetCore.Extensions; +using JsonApiDotNetCore.Internal; using JsonApiDotNetCore.Models; using Newtonsoft.Json; using JsonApiDotNetCore.Managers.Contracts; @@ -54,11 +55,22 @@ public class ResponseSerializer : BaseDocumentBuilder, IJsonApiSerial /// public string Serialize(object data) { + if (data == null || data is IIdentifiable) + { + return SerializeSingle((IIdentifiable)data); + } + + if (data is IEnumerable collectionOfIdentifiable) + { + return SerializeMany(collectionOfIdentifiable); + } + if (data is ErrorDocument errorDocument) + { return SerializeErrorDocument(errorDocument); - if (data is IEnumerable entities) - return SerializeMany(entities); - return SerializeSingle((IIdentifiable)data); + } + + throw new InvalidOperationException("Data being returned must be errors or resources."); } private string SerializeErrorDocument(ErrorDocument errorDocument) @@ -102,11 +114,11 @@ internal string SerializeSingle(IIdentifiable entity) /// /// This method is set internal instead of private for easier testability. /// - internal string SerializeMany(IEnumerable entities) + internal string SerializeMany(IEnumerable entities) { var (attributes, relationships) = GetFieldsToSerialize(); var document = Build(entities, attributes, relationships); - foreach (ResourceObject resourceObject in (IEnumerable)document.Data) + foreach (ResourceObject resourceObject in document.ManyData) { var links = _linkBuilder.GetResourceLinks(resourceObject.Type, resourceObject.Id); if (links == null) diff --git a/test/JsonApiDotNetCoreExampleTests/Acceptance/ActionResultTests.cs b/test/JsonApiDotNetCoreExampleTests/Acceptance/ActionResultTests.cs index bc337196eb..b71ee6a6db 100644 --- a/test/JsonApiDotNetCoreExampleTests/Acceptance/ActionResultTests.cs +++ b/test/JsonApiDotNetCoreExampleTests/Acceptance/ActionResultTests.cs @@ -77,5 +77,41 @@ public async Task Empty_ActionResult_Is_Converted_To_Error_Collection() Assert.Equal("NotFound", errorDocument.Errors[0].Title); Assert.Null(errorDocument.Errors[0].Detail); } + + [Fact] + public async Task ActionResult_With_String_Object_Is_Converted_To_Error_Collection() + { + // Arrange + var route = "/abstract/123"; + var request = new HttpRequestMessage(HttpMethod.Patch, route); + var content = new + { + data = new + { + type = "todoItems", + id = 123, + attributes = new Dictionary + { + {"ordinal", 1} + } + } + }; + + request.Content = new StringContent(JsonConvert.SerializeObject(content)); + request.Content.Headers.ContentType = new MediaTypeHeaderValue(HeaderConstants.MediaType); + + // Act + var response = await _fixture.Client.SendAsync(request); + + // Assert + var body = await response.Content.ReadAsStringAsync(); + Assert.Equal(HttpStatusCode.InternalServerError, response.StatusCode); + + var errorDocument = JsonConvert.DeserializeObject(body); + Assert.Single(errorDocument.Errors); + Assert.Equal(HttpStatusCode.InternalServerError, errorDocument.Errors[0].StatusCode); + Assert.Equal("An unhandled error occurred while processing this request.", errorDocument.Errors[0].Title); + Assert.Equal("Data being returned must be errors or resources.", errorDocument.Errors[0].Detail); + } } } diff --git a/test/UnitTests/Serialization/SerializerTestsSetup.cs b/test/UnitTests/Serialization/SerializerTestsSetup.cs index 6bcf5d4b83..e7b5c0fe9f 100644 --- a/test/UnitTests/Serialization/SerializerTestsSetup.cs +++ b/test/UnitTests/Serialization/SerializerTestsSetup.cs @@ -115,7 +115,7 @@ public new Document Build(IIdentifiable entity, IReadOnlyCollection attributes = null, IReadOnlyCollection relationships = null) + public new Document Build(IEnumerable entities, IReadOnlyCollection attributes = null, IReadOnlyCollection relationships = null) { return base.Build(entities, attributes, relationships); } From 919af7331cf3e0128939e2d8988510e4c154b2db Mon Sep 17 00:00:00 2001 From: Bart Koelman Date: Thu, 14 May 2020 14:44:13 +0200 Subject: [PATCH 2/2] Empty commit to restart cibuild