Skip to content

Commit bf9e4c8

Browse files
author
Chris Santero
committed
allow controller actions to specify top-level metadata
1 parent 06e88a1 commit bf9e4c8

9 files changed

Lines changed: 150 additions & 2 deletions

File tree

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
using System;
2+
using System.Collections.Generic;
3+
using System.Linq;
4+
using System.Web;
5+
using System.Web.Http;
6+
using JSONAPI.EntityFramework.Tests.TestWebApp.Models;
7+
using Newtonsoft.Json.Linq;
8+
9+
namespace JSONAPI.EntityFramework.Tests.TestWebApp.Controllers
10+
{
11+
public class PresidentsController : ApiController
12+
{
13+
public class MyArrayPayload<T> : IPayload
14+
{
15+
private readonly T[] _array;
16+
17+
public MyArrayPayload(T[] array)
18+
{
19+
_array = array;
20+
}
21+
22+
public object PrimaryData { get { return _array; } }
23+
24+
public JObject Metadata
25+
{
26+
get
27+
{
28+
var obj = new JObject();
29+
obj["count"] = _array.Length;
30+
return obj;
31+
}
32+
}
33+
}
34+
35+
// This endpoint exists to demonstrate returning IPayload
36+
[Route("presidents")]
37+
public IHttpActionResult GetPresidents()
38+
{
39+
var users = new[]
40+
{
41+
new User
42+
{
43+
Id = "6500",
44+
FirstName = "George",
45+
LastName = "Washington"
46+
},
47+
new User
48+
{
49+
Id = "6501",
50+
FirstName = "Abraham",
51+
LastName = "Lincoln"
52+
}
53+
};
54+
55+
var payload = new MyArrayPayload<User>(users);
56+
return Ok(payload);
57+
}
58+
}
59+
}

JSONAPI.EntityFramework.Tests.TestWebApp/Controllers/UsersController.cs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
1-
using JSONAPI.Core;
1+
using System.Threading.Tasks;
2+
using System.Web.Http;
3+
using JSONAPI.Core;
24
using JSONAPI.EntityFramework.Http;
35
using JSONAPI.EntityFramework.Tests.TestWebApp.Models;
46

JSONAPI.EntityFramework.Tests.TestWebApp/JSONAPI.EntityFramework.Tests.TestWebApp.csproj

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -115,6 +115,7 @@
115115
<Content Include="Web.config" />
116116
</ItemGroup>
117117
<ItemGroup>
118+
<Compile Include="Controllers\PresidentsController.cs" />
118119
<Compile Include="Controllers\SearchController.cs" />
119120
<Compile Include="Controllers\CommentsController.cs" />
120121
<Compile Include="Controllers\UserGroupsController.cs" />
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
{
2+
"data": [
3+
{
4+
"type": "users",
5+
"id": "6500",
6+
"firstName": "George",
7+
"lastName": "Washington",
8+
"links": {
9+
"comments": [ ],
10+
"posts": [ ],
11+
"userGroups": [ ]
12+
}
13+
},
14+
{
15+
"type": "users",
16+
"id": "6501",
17+
"firstName": "Abraham",
18+
"lastName": "Lincoln",
19+
"links": {
20+
"comments": [ ],
21+
"posts": [ ],
22+
"userGroups": [ ]
23+
}
24+
}
25+
],
26+
"meta": {
27+
"count": 2
28+
}
29+
}
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
using System.Net;
2+
using System.Threading.Tasks;
3+
using Microsoft.VisualStudio.TestTools.UnitTesting;
4+
5+
namespace JSONAPI.EntityFramework.Tests.Acceptance
6+
{
7+
[TestClass]
8+
public class PayloadTests : AcceptanceTestsBase
9+
{
10+
[TestMethod]
11+
public async Task Get_returns_IPayload()
12+
{
13+
using (var effortConnection = GetEffortConnection())
14+
{
15+
var response = await SubmitGet(effortConnection, "presidents");
16+
17+
await AssertResponseContent(response, @"Acceptance\Fixtures\Payload\Responses\GetReturnsIPayloadResponse.json", HttpStatusCode.OK);
18+
}
19+
}
20+
}
21+
}

JSONAPI.EntityFramework.Tests/JSONAPI.EntityFramework.Tests.csproj

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -111,6 +111,7 @@
111111
</ItemGroup>
112112
<ItemGroup>
113113
<Compile Include="Acceptance\HeterogeneousTests.cs" />
114+
<Compile Include="Acceptance\PayloadTests.cs" />
114115
<Compile Include="Acceptance\UserGroupsTests.cs" />
115116
<Compile Include="Acceptance\PostsTests.cs" />
116117
<Compile Include="Acceptance\AcceptanceTestsBase.cs" />
@@ -175,6 +176,7 @@
175176
<EmbeddedResource Include="Acceptance\Fixtures\Posts\Responses\PatchWithArrayForToOneLinkageResponse.json" />
176177
<EmbeddedResource Include="Acceptance\Fixtures\Posts\Requests\PatchWithArrayForToOneLinkageRequest.json" />
177178
<EmbeddedResource Include="Acceptance\Fixtures\Posts\Requests\PatchWithStringForToOneLinkageRequest.json" />
179+
<EmbeddedResource Include="Acceptance\Fixtures\Payload\Responses\GetReturnsIPayloadResponse.json" />
178180
<None Include="App.Config">
179181
<SubType>Designer</SubType>
180182
</None>

JSONAPI/IPayload.cs

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
using Newtonsoft.Json.Linq;
2+
3+
namespace JSONAPI
4+
{
5+
/// <summary>
6+
/// Actions may return objects that implement this interface in order to
7+
/// add metadata to the response.
8+
/// </summary>
9+
public interface IPayload
10+
{
11+
/// <summary>
12+
/// The primary data of the response
13+
/// </summary>
14+
object PrimaryData { get; }
15+
16+
/// <summary>
17+
/// Metadata to serialize at the top-level of the response
18+
/// </summary>
19+
JObject Metadata { get; }
20+
}
21+
}

JSONAPI/JSONAPI.csproj

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,7 @@
9494
<Compile Include="Extensions\TypeExtensions.cs" />
9595
<Compile Include="Http\ApiController.cs" />
9696
<Compile Include="Http\PascalizedControllerSelector.cs" />
97+
<Compile Include="IPayload.cs" />
9798
<Compile Include="Json\ErrorSerializer.cs" />
9899
<Compile Include="Json\GuidErrorIdProvider.cs" />
99100
<Compile Include="Json\IErrorIdProvider.cs" />

JSONAPI/Json/JsonApiFormatter.cs

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -106,7 +106,7 @@ public override Task WriteToStreamAsync(System.Type type, object value, Stream w
106106
this.RelationAggregators[writeStream] = aggregator;
107107
}
108108
}
109-
109+
110110
var contentHeaders = content == null ? null : content.Headers;
111111
var effectiveEncoding = SelectCharacterEncoding(contentHeaders);
112112
JsonWriter writer = this.CreateJsonWriter(typeof(object), writeStream, effectiveEncoding);
@@ -118,6 +118,12 @@ public override Task WriteToStreamAsync(System.Type type, object value, Stream w
118118
}
119119
else
120120
{
121+
var payload = value as IPayload;
122+
if (payload != null)
123+
{
124+
value = payload.PrimaryData;
125+
}
126+
121127
writer.WriteStartObject();
122128
writer.WritePropertyName(PrimaryDataKeyName);
123129

@@ -152,6 +158,12 @@ public override Task WriteToStreamAsync(System.Type type, object value, Stream w
152158
SerializeLinkedResources(writeStream, writer, serializer, aggregator);
153159
}
154160

161+
if (payload != null && payload.Metadata != null)
162+
{
163+
writer.WritePropertyName("meta");
164+
serializer.Serialize(writer, payload.Metadata);
165+
}
166+
155167
writer.WriteEndObject();
156168
}
157169
writer.Flush();

0 commit comments

Comments
 (0)