Skip to content

Commit ba589c2

Browse files
committed
WIP: PartialJsonOutputFormatter implementation
1 parent 2082558 commit ba589c2

3 files changed

Lines changed: 130 additions & 21 deletions

File tree

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
// Copyright (c) Arjen Post. See License.txt and Notice.txt in the project root for license information.
2+
3+
using System;
4+
5+
namespace Microsoft.AspNetCore.Http
6+
{
7+
public static class HttpRequestExtensions
8+
{
9+
private const string BypassPartialResponse = "BypassPartialResponse";
10+
11+
/// <summary>
12+
/// Sets a value indicating whether partial response should be bypassed.
13+
/// </summary>
14+
/// <param name="value">The value.</param>
15+
/// <param name="request">The HTTP request.</param>
16+
public static void SetBypassPartialResponse(this HttpRequest request, bool value)
17+
{
18+
if (request == null)
19+
{
20+
throw new ArgumentNullException("request");
21+
}
22+
23+
request.HttpContext.Items.Add(BypassPartialResponse, value);
24+
}
25+
26+
/// <summary>
27+
/// Gets a value indicating whether partial response should be bypassed.
28+
/// </summary>
29+
/// <param name="request">The HTTP request.</param>
30+
/// <returns>True if partial response should be bypassed, otherwise false.</returns>
31+
public static bool GetBypassPartialResponse(this HttpRequest request)
32+
{
33+
if (request == null)
34+
{
35+
throw new ArgumentNullException("request");
36+
}
37+
38+
object value;
39+
40+
if (request.HttpContext.Items.TryGetValue(BypassPartialResponse, out value))
41+
{
42+
return (bool)value;
43+
}
44+
45+
return false;
46+
}
47+
}
48+
}

src/PartialResponse.AspNetCore.Mvc.Formatters.Json/PartialJsonOutputFormatter.cs

Lines changed: 79 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -3,11 +3,14 @@
33
using System;
44
using System.Buffers;
55
using System.IO;
6+
using System.Linq;
67
using System.Text;
78
using System.Threading.Tasks;
9+
using Microsoft.AspNetCore.Http;
810
using Microsoft.AspNetCore.Mvc.Formatters;
911
using Newtonsoft.Json;
1012
using PartialResponse.AspNetCore.Mvc.Formatters.Json.Internal;
13+
using PartialResponse.Core;
1114

1215
namespace PartialResponse.AspNetCore.Mvc.Formatters
1316
{
@@ -61,26 +64,6 @@ public PartialJsonOutputFormatter(JsonSerializerSettings serializerSettings, Arr
6164
/// </remarks>
6265
protected JsonSerializerSettings SerializerSettings { get; }
6366

64-
/// <summary>
65-
/// Writes the given <paramref name="value"/> as JSON using the given
66-
/// <paramref name="writer"/>.
67-
/// </summary>
68-
/// <param name="writer">The <see cref="TextWriter"/> used to write the <paramref name="value"/></param>
69-
/// <param name="value">The value to write as JSON.</param>
70-
public void WriteObject(TextWriter writer, object value)
71-
{
72-
if (writer == null)
73-
{
74-
throw new ArgumentNullException(nameof(writer));
75-
}
76-
77-
using (var jsonWriter = CreateJsonWriter(writer))
78-
{
79-
var jsonSerializer = CreateJsonSerializer();
80-
jsonSerializer.Serialize(jsonWriter, value);
81-
}
82-
}
83-
8467
/// <summary>
8568
/// Called during serialization to create the <see cref="JsonWriter"/>.
8669
/// </summary>
@@ -116,6 +99,62 @@ protected virtual JsonSerializer CreateJsonSerializer()
11699
return _serializer;
117100
}
118101

102+
/// <summary>
103+
/// Gets a list of partial response fields for the current request.
104+
/// </summary>
105+
/// <param name="request">The request.</param>
106+
/// <returns>A <see cref="System.Collections.ObjectModel.Collection{string}"/> that contains the specified fields for the
107+
/// current request, or null if all fields should by serialized.</returns>
108+
protected virtual Fields GetPartialResponseFields(HttpRequest request)
109+
{
110+
if (request == null)
111+
{
112+
throw new ArgumentNullException("request");
113+
}
114+
115+
var queryOption = request.Query["fields"].FirstOrDefault();
116+
117+
if (queryOption != null)
118+
{
119+
Fields fields;
120+
121+
if (!Fields.TryParse(queryOption, out fields))
122+
{
123+
// TODO: No more HttpResponseException in ASP.NET Core.
124+
throw new Exception();
125+
}
126+
127+
return fields;
128+
}
129+
130+
return null;
131+
}
132+
133+
/// <summary>
134+
/// Returns a value that indicates whether partial response should be bypassed.
135+
/// </summary>
136+
/// <param name="request">The request.</param>
137+
/// <returns>True if the partial response should be bypassed, otherwise false.</returns>
138+
protected virtual bool ShouldBypassPartialResponse(HttpRequest request)
139+
{
140+
if (request == null)
141+
{
142+
throw new ArgumentNullException("request");
143+
}
144+
145+
if (request.GetBypassPartialResponse())
146+
{
147+
return true;
148+
}
149+
150+
if (request.HttpContext != null)
151+
{
152+
return request.HttpContext.Response.StatusCode != 200;
153+
}
154+
155+
return false;
156+
}
157+
119158
/// <inheritdoc />
120159
public override async Task WriteResponseBodyAsync(OutputFormatterWriteContext context, Encoding selectedEncoding)
121160
{
@@ -132,7 +171,26 @@ public override async Task WriteResponseBodyAsync(OutputFormatterWriteContext co
132171
var response = context.HttpContext.Response;
133172
using (var writer = context.WriterFactory(response.Body, selectedEncoding))
134173
{
135-
WriteObject(writer, context.Object);
174+
using (var jsonWriter = CreateJsonWriter(writer))
175+
{
176+
var jsonSerializer = CreateJsonSerializer();
177+
178+
Fields fields = null;
179+
180+
if (!ShouldBypassPartialResponse(context.HttpContext.Request))
181+
{
182+
fields = GetPartialResponseFields(context.HttpContext.Request);
183+
}
184+
185+
if (fields == null)
186+
{
187+
jsonSerializer.Serialize(jsonWriter, context.Object);
188+
}
189+
else
190+
{
191+
PartialJsonUtilities.RemovePropertiesAndArrayElements(context.Object, jsonWriter, jsonSerializer, value => fields.Matches(value));
192+
}
193+
}
136194

137195
// Perf: call FlushAsync to call WriteAsync on the stream with any content left in the TextWriter's
138196
// buffers. This is better than just letting dispose handle it (which would result in a synchronous

src/PartialResponse.AspNetCore.Mvc.Formatters.Json/project.json

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,9 @@
1818
"warningsAsErrors": true
1919
},
2020
"dependencies": {
21+
"PartialResponse.Core": {
22+
"target": "project"
23+
},
2124
"Microsoft.AspNetCore.Mvc.Core": "1.1.0",
2225
"NETStandard.Library": "1.6.1",
2326
"Newtonsoft.Json": "9.0.1"

0 commit comments

Comments
 (0)