Skip to content

Commit ea75569

Browse files
committed
allow case-insensitive matching
1 parent 4473e98 commit ea75569

6 files changed

Lines changed: 121 additions & 14 deletions

File tree

src/PartialResponse.AspNetCore.Mvc.Formatters.Json/Internal/MvcPartialJsonMvcOptionsSetup.cs

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,6 @@
88
using Microsoft.Extensions.ObjectPool;
99
using Microsoft.Extensions.Options;
1010
using Microsoft.Net.Http.Headers;
11-
using Newtonsoft.Json;
1211
using Newtonsoft.Json.Linq;
1312

1413
namespace PartialResponse.AspNetCore.Mvc.Formatters.Json.Internal
@@ -19,7 +18,7 @@ namespace PartialResponse.AspNetCore.Mvc.Formatters.Json.Internal
1918
public class MvcPartialJsonMvcOptionsSetup : IConfigureOptions<MvcOptions>
2019
{
2120
private readonly ILoggerFactory _loggerFactory;
22-
private readonly JsonSerializerSettings _jsonSerializerSettings;
21+
private readonly MvcPartialJsonOptions _partialJsonOptions;
2322
private readonly ArrayPool<char> _charPool;
2423
private readonly ObjectPoolProvider _objectPoolProvider;
2524

@@ -50,14 +49,14 @@ public MvcPartialJsonMvcOptionsSetup(
5049
}
5150

5251
_loggerFactory = loggerFactory;
53-
_jsonSerializerSettings = partialJsonOptions.Value.SerializerSettings;
52+
_partialJsonOptions = partialJsonOptions.Value;
5453
_charPool = charPool;
5554
_objectPoolProvider = objectPoolProvider;
5655
}
5756

5857
public void Configure(MvcOptions options)
5958
{
60-
options.OutputFormatters.Add(new PartialJsonOutputFormatter(_jsonSerializerSettings, _charPool));
59+
options.OutputFormatters.Add(new PartialJsonOutputFormatter(_partialJsonOptions.SerializerSettings, _charPool, _partialJsonOptions.IgnoreCase));
6160

6261
// TODO: Remove?
6362
options.FormatterMappings.SetMediaTypeMappingForFormat("json", MediaTypeHeaderValue.Parse("application/json"));

src/PartialResponse.AspNetCore.Mvc.Formatters.Json/Internal/PartialJsonResultExecutor.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -141,7 +141,7 @@ public Task ExecuteAsync(ActionContext context, PartialJsonResult result)
141141

142142
if (fields.HasValue)
143143
{
144-
jsonSerializer.Serialize(jsonWriter, result.Value, path => fields.Value.Matches(path));
144+
jsonSerializer.Serialize(jsonWriter, result.Value, path => fields.Value.Matches(path, this.Options.IgnoreCase));
145145
}
146146
else
147147
{

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

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,11 @@ namespace PartialResponse.AspNetCore.Mvc
1010
/// </summary>
1111
public class MvcPartialJsonOptions
1212
{
13+
/// <summary>
14+
/// Gets a value that indicates whether partial response allows case-insensitive matching.
15+
/// </summary>
16+
public bool IgnoreCase { get; set; }
17+
1318
/// <summary>
1419
/// Gets the <see cref="JsonSerializerSettings"/> that are used by this application.
1520
/// </summary>

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

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ namespace PartialResponse.AspNetCore.Mvc.Formatters
1919
public class PartialJsonOutputFormatter : TextOutputFormatter
2020
{
2121
private readonly IArrayPool<char> _charPool;
22+
private readonly bool _ignoreCase;
2223

2324
// Perf: JsonSerializers are relatively expensive to create, and are thread safe. We cache
2425
// the serializer and invalidate it when the settings change.
@@ -33,7 +34,8 @@ public class PartialJsonOutputFormatter : TextOutputFormatter
3334
/// <see cref="JsonSerializerSettingsProvider.CreateSerializerSettings"/> initially returned.
3435
/// </param>
3536
/// <param name="charPool">The <see cref="ArrayPool{Char}"/>.</param>
36-
public PartialJsonOutputFormatter(JsonSerializerSettings serializerSettings, ArrayPool<char> charPool)
37+
/// <param name="ignoreCase">A value that indicates whether partial response allows case-insensitive matching.</param>
38+
public PartialJsonOutputFormatter(JsonSerializerSettings serializerSettings, ArrayPool<char> charPool, bool ignoreCase)
3739
{
3840
if (serializerSettings == null)
3941
{
@@ -47,6 +49,7 @@ public PartialJsonOutputFormatter(JsonSerializerSettings serializerSettings, Arr
4749

4850
SerializerSettings = serializerSettings;
4951
_charPool = new JsonArrayPool<char>(charPool);
52+
_ignoreCase = ignoreCase;
5053

5154
SupportedEncodings.Add(Encoding.UTF8);
5255
SupportedEncodings.Add(Encoding.Unicode);
@@ -142,7 +145,7 @@ private void WriteObject(TextWriter writer, object value, Fields? fields)
142145

143146
if (fields.HasValue)
144147
{
145-
jsonSerializer.Serialize(jsonWriter, value, path => fields.Value.Matches(path));
148+
jsonSerializer.Serialize(jsonWriter, value, path => fields.Value.Matches(path, _ignoreCase));
146149
}
147150
else
148151
{

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

Lines changed: 56 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,6 @@ namespace PartialResponse.AspNetCore.Mvc.Formatters.Json
1212
{
1313
public class PartialJsonOutputFormatterTests
1414
{
15-
private readonly PartialJsonOutputFormatter formatter;
1615
private readonly HttpContext httpContext = Mock.Of<HttpContext>();
1716
private readonly HttpRequest httpRequest = Mock.Of<HttpRequest>();
1817
private readonly HttpResponse httpResponse = Mock.Of<HttpResponse>();
@@ -32,8 +31,6 @@ public PartialJsonOutputFormatterTests()
3231
Mock.Get(this.httpContext)
3332
.SetupGet(httpContext => httpContext.Response)
3433
.Returns(this.httpResponse);
35-
36-
this.formatter = new PartialJsonOutputFormatter(new JsonSerializerSettings(), Mock.Of<ArrayPool<char>>());
3734
}
3835

3936
[Fact]
@@ -49,9 +46,10 @@ public async Task TheWriteResponseBodyAsyncMethodShouldReturnStatusCode400IfFiel
4946
.Returns("foo/");
5047

5148
var writeContext = new OutputFormatterWriteContext(this.httpContext, (stream, encoding) => new StringWriter(this.body), typeof(object), new {});
49+
var formatter = new PartialJsonOutputFormatter(new JsonSerializerSettings(), Mock.Of<ArrayPool<char>>(), false);
5250

5351
// Act
54-
await this.formatter.WriteResponseBodyAsync(writeContext, Encoding.UTF8);
52+
await formatter.WriteResponseBodyAsync(writeContext, Encoding.UTF8);
5553

5654
// Assert
5755
Mock.Get(this.httpResponse)
@@ -71,9 +69,10 @@ public async Task TheWriteResponseBodyAsyncMethodShouldNotWriteBodyIfFieldsMalfo
7169
.Returns("foo/");
7270

7371
var writeContext = new OutputFormatterWriteContext(this.httpContext, (stream, encoding) => new StringWriter(this.body), typeof(object), new {});
72+
var formatter = new PartialJsonOutputFormatter(new JsonSerializerSettings(), Mock.Of<ArrayPool<char>>(), false);
7473

7574
// Act
76-
await this.formatter.WriteResponseBodyAsync(writeContext, Encoding.UTF8);
75+
await formatter.WriteResponseBodyAsync(writeContext, Encoding.UTF8);
7776

7877
// Assert
7978
Assert.Equal(0, this.body.Length);
@@ -90,9 +89,10 @@ public async Task TheWriteResponseBodyAsyncMethodShouldNotApplyFieldsIfNotSuppli
9089
var value = new { foo = "bar" };
9190

9291
var writeContext = new OutputFormatterWriteContext(this.httpContext, (stream, encoding) => new StringWriter(this.body), typeof(object), value);
92+
var formatter = new PartialJsonOutputFormatter(new JsonSerializerSettings(), Mock.Of<ArrayPool<char>>(), false);
9393

9494
// Act
95-
await this.formatter.WriteResponseBodyAsync(writeContext, Encoding.UTF8);
95+
await formatter.WriteResponseBodyAsync(writeContext, Encoding.UTF8);
9696

9797
// Assert
9898
Assert.Equal("{\"foo\":\"bar\"}", this.body.ToString());
@@ -113,12 +113,61 @@ public async Task TheWriteResponseBodyAsyncMethodShouldApplyFieldsIfSupplied()
113113
var value = new { foo = "bar", baz = "qux" };
114114

115115
var writeContext = new OutputFormatterWriteContext(this.httpContext, (stream, encoding) => new StringWriter(this.body), typeof(object), value);
116+
var formatter = new PartialJsonOutputFormatter(new JsonSerializerSettings(), Mock.Of<ArrayPool<char>>(), false);
117+
118+
// Act
119+
await formatter.WriteResponseBodyAsync(writeContext, Encoding.UTF8);
120+
121+
// Assert
122+
Assert.Equal("{\"foo\":\"bar\"}", this.body.ToString());
123+
}
124+
125+
[Fact]
126+
public async Task TheWriteResponseBodyAsyncMethodShouldIgnoreCase()
127+
{
128+
// Arrange
129+
Mock.Get(this.queryCollection)
130+
.Setup(queryCollection => queryCollection.ContainsKey("fields"))
131+
.Returns(true);
132+
133+
Mock.Get(this.queryCollection)
134+
.SetupGet(queryCollection => queryCollection["fields"])
135+
.Returns("FOO");
136+
137+
var value = new { foo = "bar" };
138+
139+
var writeContext = new OutputFormatterWriteContext(this.httpContext, (stream, encoding) => new StringWriter(this.body), typeof(object), value);
140+
var formatter = new PartialJsonOutputFormatter(new JsonSerializerSettings(), Mock.Of<ArrayPool<char>>(), true);
116141

117142
// Act
118-
await this.formatter.WriteResponseBodyAsync(writeContext, Encoding.UTF8);
143+
await formatter.WriteResponseBodyAsync(writeContext, Encoding.UTF8);
119144

120145
// Assert
121146
Assert.Equal("{\"foo\":\"bar\"}", this.body.ToString());
122147
}
148+
149+
[Fact]
150+
public async Task TheWriteResponseBodyAsyncMethodShouldNotIgnoreCase()
151+
{
152+
// Arrange
153+
Mock.Get(this.queryCollection)
154+
.Setup(queryCollection => queryCollection.ContainsKey("fields"))
155+
.Returns(true);
156+
157+
Mock.Get(this.queryCollection)
158+
.SetupGet(queryCollection => queryCollection["fields"])
159+
.Returns("FOO");
160+
161+
var value = new { foo = "bar" };
162+
163+
var writeContext = new OutputFormatterWriteContext(this.httpContext, (stream, encoding) => new StringWriter(this.body), typeof(object), value);
164+
var formatter = new PartialJsonOutputFormatter(new JsonSerializerSettings(), Mock.Of<ArrayPool<char>>(), false);
165+
166+
// Act
167+
await formatter.WriteResponseBodyAsync(writeContext, Encoding.UTF8);
168+
169+
// Assert
170+
Assert.Equal("{}", this.body.ToString());
171+
}
123172
}
124173
}

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

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ public class PartialJsonResultExecutorTests
2121
private readonly IHttpResponseStreamWriterFactory writerFactory = Mock.Of<IHttpResponseStreamWriterFactory>();
2222
private readonly ILogger<PartialJsonResultExecutor> logger = Mock.Of<ILogger<PartialJsonResultExecutor>>();
2323
private readonly IOptions<MvcPartialJsonOptions> options = Mock.Of<IOptions<MvcPartialJsonOptions>>();
24+
private readonly MvcPartialJsonOptions partialJsonOptions = new MvcPartialJsonOptions();
2425
private readonly HttpContext httpContext = Mock.Of<HttpContext>();
2526
private readonly HttpRequest httpRequest = Mock.Of<HttpRequest>();
2627
private readonly HttpResponse httpResponse = Mock.Of<HttpResponse>();
@@ -45,6 +46,10 @@ public PartialJsonResultExecutorTests()
4546
.Setup(writerFactory => writerFactory.CreateWriter(It.IsAny<Stream>(), It.IsAny<Encoding>()))
4647
.Returns(new StringWriter(this.body));
4748

49+
Mock.Get(this.options)
50+
.SetupGet(options => options.Value)
51+
.Returns(this.partialJsonOptions);
52+
4853
this.executor = new PartialJsonResultExecutor(this.writerFactory, this.logger, this.options, Mock.Of<ArrayPool<char>>());
4954
this.actionContext = new ActionContext() { HttpContext = this.httpContext };
5055
}
@@ -129,5 +134,51 @@ public async Task TheExecuteAsyncMethodShouldApplyFieldsIfSupplied()
129134
// Assert
130135
Assert.Equal("{\"foo\":\"bar\"}", this.body.ToString());
131136
}
137+
138+
[Fact]
139+
public async Task TheExecuteAsyncMethodShouldIgnoreCase()
140+
{
141+
// Arrange
142+
Mock.Get(this.queryCollection)
143+
.Setup(queryCollection => queryCollection.ContainsKey("fields"))
144+
.Returns(true);
145+
146+
Mock.Get(this.queryCollection)
147+
.SetupGet(queryCollection => queryCollection["fields"])
148+
.Returns("FOO");
149+
150+
this.partialJsonOptions.IgnoreCase = true;
151+
152+
var partialJsonResult = new PartialJsonResult(new { foo = "bar", baz = "qux" }, new JsonSerializerSettings());
153+
154+
// Act
155+
await this.executor.ExecuteAsync(this.actionContext, partialJsonResult);
156+
157+
// Assert
158+
Assert.Equal("{\"foo\":\"bar\"}", this.body.ToString());
159+
}
160+
161+
[Fact]
162+
public async Task TheExecuteAsyncMethodShouldNotIgnoreCase()
163+
{
164+
// Arrange
165+
Mock.Get(this.queryCollection)
166+
.Setup(queryCollection => queryCollection.ContainsKey("fields"))
167+
.Returns(true);
168+
169+
Mock.Get(this.queryCollection)
170+
.SetupGet(queryCollection => queryCollection["fields"])
171+
.Returns("FOO");
172+
173+
this.partialJsonOptions.IgnoreCase = false;
174+
175+
var partialJsonResult = new PartialJsonResult(new { foo = "bar", baz = "qux" }, new JsonSerializerSettings());
176+
177+
// Act
178+
await this.executor.ExecuteAsync(this.actionContext, partialJsonResult);
179+
180+
// Assert
181+
Assert.Equal("{}", this.body.ToString());
182+
}
132183
}
133184
}

0 commit comments

Comments
 (0)