Skip to content

Commit 8d74f0f

Browse files
committed
add option to disable partial response for the current request
1 parent ea75569 commit 8d74f0f

3 files changed

Lines changed: 126 additions & 33 deletions

File tree

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

Lines changed: 5 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -1,49 +1,25 @@
11
// Copyright (c) Arjen Post. See License.txt and Notice.txt in the project root for license information.
22

33
using System;
4+
using PartialResponse.AspNetCore.Mvc.Formatters;
45
using PartialResponse.Core;
56

67
namespace Microsoft.AspNetCore.Http
78
{
89
public static class HttpRequestExtensions
910
{
10-
private const string BypassPartialResponse = "BypassPartialResponse";
11-
1211
/// <summary>
13-
/// Sets a value indicating whether partial response should be bypassed.
12+
/// Indicates that partial response should be bypassed for the current request.
1413
/// </summary>
15-
/// <param name="value">The value.</param>
16-
/// <param name="request">The HTTP request.</param>
17-
public static void SetBypassPartialResponse(this HttpRequest request, bool value)
14+
/// <param name="request">The <see cref="HttpRequest"/>.</param>
15+
public static void BypassPartialResponse(this HttpRequest request)
1816
{
1917
if (request == null)
2018
{
2119
throw new ArgumentNullException("request");
2220
}
2321

24-
request.HttpContext.Items.Add(BypassPartialResponse, value);
25-
}
26-
27-
/// <summary>
28-
/// Gets a value indicating whether partial response should be bypassed.
29-
/// </summary>
30-
/// <param name="request">The HTTP request.</param>
31-
/// <returns>True if partial response should be bypassed, otherwise false.</returns>
32-
public static bool GetBypassPartialResponse(this HttpRequest request)
33-
{
34-
if (request == null)
35-
{
36-
throw new ArgumentNullException("request");
37-
}
38-
39-
object value;
40-
41-
if (request.HttpContext.Items.TryGetValue(BypassPartialResponse, out value))
42-
{
43-
return (bool)value;
44-
}
45-
46-
return false;
22+
request.HttpContext.Items[PartialJsonOutputFormatter.BypassPartialResponseKey] = null;
4723
}
4824

4925
internal static bool TryGetFields(this HttpRequest request, out Fields? result)

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

Lines changed: 29 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,8 @@ namespace PartialResponse.AspNetCore.Mvc.Formatters
1818
/// </summary>
1919
public class PartialJsonOutputFormatter : TextOutputFormatter
2020
{
21+
internal const string BypassPartialResponseKey = "BypassPartialResponse";
22+
2123
private readonly IArrayPool<char> _charPool;
2224
private readonly bool _ignoreCase;
2325

@@ -101,6 +103,26 @@ protected virtual JsonSerializer CreateJsonSerializer()
101103
return _serializer;
102104
}
103105

106+
/// <summary>
107+
/// Returns a value that indicates whether partial response should be bypassed for the current request.
108+
/// </summary>
109+
/// <param name="httpContext">The <see cref="HttpContext"/>.</param>
110+
/// <returns>true if the partial response should be bypassed; otherwise, false.</returns>
111+
protected virtual bool ShouldBypassPartialResponse(HttpContext httpContext)
112+
{
113+
if (httpContext == null)
114+
{
115+
throw new ArgumentNullException(nameof(httpContext));
116+
}
117+
118+
if (httpContext.Items.ContainsKey(BypassPartialResponseKey))
119+
{
120+
return true;
121+
}
122+
123+
return httpContext.Response.StatusCode != 200;
124+
}
125+
104126
/// <inheritdoc />
105127
public override async Task WriteResponseBodyAsync(OutputFormatterWriteContext context, Encoding selectedEncoding)
106128
{
@@ -117,13 +139,16 @@ public override async Task WriteResponseBodyAsync(OutputFormatterWriteContext co
117139
var request = context.HttpContext.Request;
118140
var response = context.HttpContext.Response;
119141

120-
Fields? fields;
142+
Fields? fields = null;
121143

122-
if (!request.TryGetFields(out fields))
144+
if (!this.ShouldBypassPartialResponse(context.HttpContext))
123145
{
124-
response.StatusCode = 400;
146+
if (!request.TryGetFields(out fields))
147+
{
148+
response.StatusCode = 400;
125149

126-
return;
150+
return;
151+
}
127152
}
128153

129154
using (var writer = context.WriterFactory(response.Body, selectedEncoding))

test/PartialResponse.AspNetCore.Mvc.Formatters.Json.Test/PartialJsonOutputFormatterTests.cs

Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
using System.Buffers;
2+
using System.Collections.Generic;
23
using System.IO;
34
using System.Text;
45
using System.Threading.Tasks;
@@ -13,6 +14,7 @@ namespace PartialResponse.AspNetCore.Mvc.Formatters.Json
1314
public class PartialJsonOutputFormatterTests
1415
{
1516
private readonly HttpContext httpContext = Mock.Of<HttpContext>();
17+
private readonly Dictionary<object, object> httpContextItems = new Dictionary<object, object>();
1618
private readonly HttpRequest httpRequest = Mock.Of<HttpRequest>();
1719
private readonly HttpResponse httpResponse = Mock.Of<HttpResponse>();
1820
private readonly IQueryCollection queryCollection = Mock.Of<IQueryCollection>();
@@ -31,12 +33,24 @@ public PartialJsonOutputFormatterTests()
3133
Mock.Get(this.httpContext)
3234
.SetupGet(httpContext => httpContext.Response)
3335
.Returns(this.httpResponse);
36+
37+
Mock.Get(this.httpContext)
38+
.SetupGet(httpContext => httpContext.Items)
39+
.Returns(this.httpContextItems);
40+
41+
Mock.Get(this.httpRequest)
42+
.SetupGet(httpRequest => httpRequest.HttpContext)
43+
.Returns(this.httpContext);
3444
}
3545

3646
[Fact]
3747
public async Task TheWriteResponseBodyAsyncMethodShouldReturnStatusCode400IfFieldsMalformed()
3848
{
3949
// Arrange
50+
Mock.Get(this.httpResponse)
51+
.SetupGet(httpResponse => httpResponse.StatusCode)
52+
.Returns(200);
53+
4054
Mock.Get(this.queryCollection)
4155
.Setup(queryCollection => queryCollection.ContainsKey("fields"))
4256
.Returns(true);
@@ -60,6 +74,10 @@ public async Task TheWriteResponseBodyAsyncMethodShouldReturnStatusCode400IfFiel
6074
public async Task TheWriteResponseBodyAsyncMethodShouldNotWriteBodyIfFieldsMalformed()
6175
{
6276
// Arrange
77+
Mock.Get(this.httpResponse)
78+
.SetupGet(httpResponse => httpResponse.StatusCode)
79+
.Returns(200);
80+
6381
Mock.Get(this.queryCollection)
6482
.Setup(queryCollection => queryCollection.ContainsKey("fields"))
6583
.Returns(true);
@@ -82,6 +100,10 @@ public async Task TheWriteResponseBodyAsyncMethodShouldNotWriteBodyIfFieldsMalfo
82100
public async Task TheWriteResponseBodyAsyncMethodShouldNotApplyFieldsIfNotSupplied()
83101
{
84102
// Arrange
103+
Mock.Get(this.httpResponse)
104+
.SetupGet(httpResponse => httpResponse.StatusCode)
105+
.Returns(200);
106+
85107
Mock.Get(this.queryCollection)
86108
.Setup(queryCollection => queryCollection.ContainsKey("fields"))
87109
.Returns(false);
@@ -102,6 +124,10 @@ public async Task TheWriteResponseBodyAsyncMethodShouldNotApplyFieldsIfNotSuppli
102124
public async Task TheWriteResponseBodyAsyncMethodShouldApplyFieldsIfSupplied()
103125
{
104126
// Arrange
127+
Mock.Get(this.httpResponse)
128+
.SetupGet(httpResponse => httpResponse.StatusCode)
129+
.Returns(200);
130+
105131
Mock.Get(this.queryCollection)
106132
.Setup(queryCollection => queryCollection.ContainsKey("fields"))
107133
.Returns(true);
@@ -126,6 +152,10 @@ public async Task TheWriteResponseBodyAsyncMethodShouldApplyFieldsIfSupplied()
126152
public async Task TheWriteResponseBodyAsyncMethodShouldIgnoreCase()
127153
{
128154
// Arrange
155+
Mock.Get(this.httpResponse)
156+
.SetupGet(httpResponse => httpResponse.StatusCode)
157+
.Returns(200);
158+
129159
Mock.Get(this.queryCollection)
130160
.Setup(queryCollection => queryCollection.ContainsKey("fields"))
131161
.Returns(true);
@@ -150,6 +180,10 @@ public async Task TheWriteResponseBodyAsyncMethodShouldIgnoreCase()
150180
public async Task TheWriteResponseBodyAsyncMethodShouldNotIgnoreCase()
151181
{
152182
// Arrange
183+
Mock.Get(this.httpResponse)
184+
.SetupGet(httpResponse => httpResponse.StatusCode)
185+
.Returns(200);
186+
153187
Mock.Get(this.queryCollection)
154188
.Setup(queryCollection => queryCollection.ContainsKey("fields"))
155189
.Returns(true);
@@ -169,5 +203,63 @@ public async Task TheWriteResponseBodyAsyncMethodShouldNotIgnoreCase()
169203
// Assert
170204
Assert.Equal("{}", this.body.ToString());
171205
}
206+
207+
[Fact]
208+
public async Task TheWriteResponseBodyAsyncMethodShouldBypassPartialResponseIfConfigured()
209+
{
210+
// Arrange
211+
Mock.Get(this.httpResponse)
212+
.SetupGet(httpResponse => httpResponse.StatusCode)
213+
.Returns(200);
214+
215+
Mock.Get(this.queryCollection)
216+
.Setup(queryCollection => queryCollection.ContainsKey("fields"))
217+
.Returns(true);
218+
219+
Mock.Get(this.queryCollection)
220+
.SetupGet(queryCollection => queryCollection["fields"])
221+
.Returns("");
222+
223+
this.httpRequest.BypassPartialResponse();
224+
225+
var value = new { foo = "bar" };
226+
227+
var writeContext = new OutputFormatterWriteContext(this.httpContext, (stream, encoding) => new StringWriter(this.body), typeof(object), value);
228+
var formatter = new PartialJsonOutputFormatter(new JsonSerializerSettings(), Mock.Of<ArrayPool<char>>(), false);
229+
230+
// Act
231+
await formatter.WriteResponseBodyAsync(writeContext, Encoding.UTF8);
232+
233+
// Assert
234+
Assert.Equal("{\"foo\":\"bar\"}", this.body.ToString());
235+
}
236+
237+
[Fact]
238+
public async Task TheWriteResponseBodyAsyncMethodShouldBypassPartialResponseIfStatusCodeIsNot200()
239+
{
240+
// Arrange
241+
Mock.Get(this.httpResponse)
242+
.SetupGet(httpResponse => httpResponse.StatusCode)
243+
.Returns(500);
244+
245+
Mock.Get(this.queryCollection)
246+
.Setup(queryCollection => queryCollection.ContainsKey("fields"))
247+
.Returns(true);
248+
249+
Mock.Get(this.queryCollection)
250+
.SetupGet(queryCollection => queryCollection["fields"])
251+
.Returns("");
252+
253+
var value = new { foo = "bar" };
254+
255+
var writeContext = new OutputFormatterWriteContext(this.httpContext, (stream, encoding) => new StringWriter(this.body), typeof(object), value);
256+
var formatter = new PartialJsonOutputFormatter(new JsonSerializerSettings(), Mock.Of<ArrayPool<char>>(), false);
257+
258+
// Act
259+
await formatter.WriteResponseBodyAsync(writeContext, Encoding.UTF8);
260+
261+
// Assert
262+
Assert.Equal("{\"foo\":\"bar\"}", this.body.ToString());
263+
}
172264
}
173265
}

0 commit comments

Comments
 (0)