Skip to content

Commit b8887c4

Browse files
author
Bart Koelman
authored
Show helpful error when ActionResult returns unsupported type (#753)
Show helpful error instead of crash when ActionResult returns unsupported type.
1 parent 286eea7 commit b8887c4

File tree

7 files changed

+60
-10
lines changed

7 files changed

+60
-10
lines changed

src/Examples/JsonApiDotNetCoreExample/Controllers/TodoItemsTestController.cs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,9 @@ public override async Task<IActionResult> PostAsync(TodoItem entity)
6161
[HttpPatch("{id}")]
6262
public override async Task<IActionResult> PatchAsync(int id, [FromBody] TodoItem entity)
6363
{
64-
return await base.PatchAsync(id, entity);
64+
await Task.Yield();
65+
66+
return Conflict("Something went wrong");
6567
}
6668

6769
[HttpPatch("{id}/relationships/{relationshipName}")]

src/JsonApiDotNetCore/Serialization/Client/IRequestSerializer.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ public interface IRequestSerializer
2222
/// </summary>
2323
/// <param name="entities">Entities to serialize</param>
2424
/// <returns>The serialized content</returns>
25-
string Serialize(IEnumerable entities);
25+
string Serialize(IEnumerable<IIdentifiable> entities);
2626
/// <summary>
2727
/// Sets the attributes that will be included in the serialized payload.
2828
/// You can use <see cref="IResourceGraph.GetAttributes{TResource}"/>

src/JsonApiDotNetCore/Serialization/Client/RequestSerializer.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@ public string Serialize(IIdentifiable entity)
4141
}
4242

4343
/// <inheritdoc/>
44-
public string Serialize(IEnumerable entities)
44+
public string Serialize(IEnumerable<IIdentifiable> entities)
4545
{
4646
IIdentifiable entity = null;
4747
foreach (IIdentifiable item in entities)

src/JsonApiDotNetCore/Serialization/Common/DocumentBuilder.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,7 @@ protected Document Build(IIdentifiable entity, IReadOnlyCollection<AttrAttribute
4444
/// <param name="attributes">Attributes to include in the building process</param>
4545
/// <param name="relationships">Relationships to include in the building process</param>
4646
/// <returns>The resource object that was built</returns>
47-
protected Document Build(IEnumerable entities, IReadOnlyCollection<AttrAttribute> attributes, IReadOnlyCollection<RelationshipAttribute> relationships)
47+
protected Document Build(IEnumerable<IIdentifiable> entities, IReadOnlyCollection<AttrAttribute> attributes, IReadOnlyCollection<RelationshipAttribute> relationships)
4848
{
4949
var data = new List<ResourceObject>();
5050
foreach (IIdentifiable entity in entities)

src/JsonApiDotNetCore/Serialization/Server/ResponseSerializer.cs

Lines changed: 17 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
using System.Collections.Generic;
44
using JsonApiDotNetCore.Configuration;
55
using JsonApiDotNetCore.Extensions;
6+
using JsonApiDotNetCore.Internal;
67
using JsonApiDotNetCore.Models;
78
using Newtonsoft.Json;
89
using JsonApiDotNetCore.Managers.Contracts;
@@ -54,11 +55,22 @@ public ResponseSerializer(IMetaBuilder<TResource> metaBuilder,
5455
/// <inheritdoc/>
5556
public string Serialize(object data)
5657
{
58+
if (data == null || data is IIdentifiable)
59+
{
60+
return SerializeSingle((IIdentifiable)data);
61+
}
62+
63+
if (data is IEnumerable<IIdentifiable> collectionOfIdentifiable)
64+
{
65+
return SerializeMany(collectionOfIdentifiable);
66+
}
67+
5768
if (data is ErrorDocument errorDocument)
69+
{
5870
return SerializeErrorDocument(errorDocument);
59-
if (data is IEnumerable entities)
60-
return SerializeMany(entities);
61-
return SerializeSingle((IIdentifiable)data);
71+
}
72+
73+
throw new InvalidOperationException("Data being returned must be errors or resources.");
6274
}
6375

6476
private string SerializeErrorDocument(ErrorDocument errorDocument)
@@ -102,11 +114,11 @@ internal string SerializeSingle(IIdentifiable entity)
102114
/// <remarks>
103115
/// This method is set internal instead of private for easier testability.
104116
/// </remarks>
105-
internal string SerializeMany(IEnumerable entities)
117+
internal string SerializeMany(IEnumerable<IIdentifiable> entities)
106118
{
107119
var (attributes, relationships) = GetFieldsToSerialize();
108120
var document = Build(entities, attributes, relationships);
109-
foreach (ResourceObject resourceObject in (IEnumerable)document.Data)
121+
foreach (ResourceObject resourceObject in document.ManyData)
110122
{
111123
var links = _linkBuilder.GetResourceLinks(resourceObject.Type, resourceObject.Id);
112124
if (links == null)

test/JsonApiDotNetCoreExampleTests/Acceptance/ActionResultTests.cs

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -77,5 +77,41 @@ public async Task Empty_ActionResult_Is_Converted_To_Error_Collection()
7777
Assert.Equal("NotFound", errorDocument.Errors[0].Title);
7878
Assert.Null(errorDocument.Errors[0].Detail);
7979
}
80+
81+
[Fact]
82+
public async Task ActionResult_With_String_Object_Is_Converted_To_Error_Collection()
83+
{
84+
// Arrange
85+
var route = "/abstract/123";
86+
var request = new HttpRequestMessage(HttpMethod.Patch, route);
87+
var content = new
88+
{
89+
data = new
90+
{
91+
type = "todoItems",
92+
id = 123,
93+
attributes = new Dictionary<string, object>
94+
{
95+
{"ordinal", 1}
96+
}
97+
}
98+
};
99+
100+
request.Content = new StringContent(JsonConvert.SerializeObject(content));
101+
request.Content.Headers.ContentType = new MediaTypeHeaderValue(HeaderConstants.MediaType);
102+
103+
// Act
104+
var response = await _fixture.Client.SendAsync(request);
105+
106+
// Assert
107+
var body = await response.Content.ReadAsStringAsync();
108+
Assert.Equal(HttpStatusCode.InternalServerError, response.StatusCode);
109+
110+
var errorDocument = JsonConvert.DeserializeObject<ErrorDocument>(body);
111+
Assert.Single(errorDocument.Errors);
112+
Assert.Equal(HttpStatusCode.InternalServerError, errorDocument.Errors[0].StatusCode);
113+
Assert.Equal("An unhandled error occurred while processing this request.", errorDocument.Errors[0].Title);
114+
Assert.Equal("Data being returned must be errors or resources.", errorDocument.Errors[0].Detail);
115+
}
80116
}
81117
}

test/UnitTests/Serialization/SerializerTestsSetup.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -115,7 +115,7 @@ public TestDocumentBuilder(IResourceObjectBuilder resourceObjectBuilder) : base(
115115
return base.Build(entity, attributes, relationships);
116116
}
117117

118-
public new Document Build(IEnumerable entities, IReadOnlyCollection<AttrAttribute> attributes = null, IReadOnlyCollection<RelationshipAttribute> relationships = null)
118+
public new Document Build(IEnumerable<IIdentifiable> entities, IReadOnlyCollection<AttrAttribute> attributes = null, IReadOnlyCollection<RelationshipAttribute> relationships = null)
119119
{
120120
return base.Build(entities, attributes, relationships);
121121
}

0 commit comments

Comments
 (0)