Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ public class ExecutorController : Controller
{
public IActionResult Index()
{
var response = new List<dynamic>()
var items = new List<dynamic>()
{
new
{
Expand Down Expand Up @@ -40,6 +40,12 @@ public IActionResult Index()
}
};

var response = new
{
Items = items,
TotalCount = items.Count
};

return this.PartialJson(response);
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,9 @@ namespace PartialResponse.AspNetCore.Mvc.Formatters.Json.Samples.Controllers
{
public class FormatterController : Controller
{
public List<dynamic> Index()
public dynamic Index()
{
return new List<dynamic>()
var items = new List<dynamic>()
{
new
{
Expand Down Expand Up @@ -39,6 +39,12 @@ public List<dynamic> Index()
}
}
};

return new
{
Items = items,
TotalCount = items.Count
};
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,11 @@ public Startup(IConfiguration configuration)

public void ConfigureServices(IServiceCollection services)
{
services.Configure<MvcPartialJsonOptions>(options => options.IgnoreCase = true);
services.Configure<MvcPartialJsonOptions>(options =>
{
options.IgnoreCase = true;
options.IgnoredFields = new[] { "totalCount" };
});

services
.AddMvc(options => options.OutputFormatters.RemoveType<JsonOutputFormatter>())
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
using Microsoft.Extensions.Options;
using Microsoft.Net.Http.Headers;
using Newtonsoft.Json;
using PartialResponse.Core;

namespace PartialResponse.AspNetCore.Mvc.Formatters.Json.Internal
{
Expand Down Expand Up @@ -138,7 +139,7 @@ public Task ExecuteAsync(ActionContext context, PartialJsonResult result)

if (fieldsParserResult.IsFieldsSet && !fieldsParserResult.HasError)
{
jsonSerializer.Serialize(jsonWriter, result.Value, path => fieldsParserResult.Fields.Matches(path, this.Options.IgnoreCase));
jsonSerializer.Serialize(jsonWriter, result.Value, path => this.ShouldSerialize(path, fieldsParserResult.Fields, this.Options));
}
else
{
Expand All @@ -149,5 +150,20 @@ public Task ExecuteAsync(ActionContext context, PartialJsonResult result)

return Task.CompletedTask;
}

private bool ShouldSerialize(string path, Fields fields, MvcPartialJsonOptions options)
{
var parsedIgnoredFields = options.ParsedIgnoredFields;

if (parsedIgnoredFields.HasValue)
{
if (parsedIgnoredFields.Value.Matches(path, options.IgnoreCase))
{
return true;
}
}

return fields.Matches(path, options.IgnoreCase);
}
}
}
Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@
// Copyright (c) Arjen Post and contributors. See LICENSE and NOTICE in the project root for license information.

using System;
using System.Collections.Generic;
using Newtonsoft.Json;
using PartialResponse.AspNetCore.Mvc.Formatters;
using PartialResponse.Core;

namespace PartialResponse.AspNetCore.Mvc
{
Expand All @@ -10,6 +13,8 @@ namespace PartialResponse.AspNetCore.Mvc
/// </summary>
public class MvcPartialJsonOptions
{
private IEnumerable<string> ignoredFields;

/// <summary>
/// Gets or sets a value indicating whether partial response allows case-insensitive matching.
/// </summary>
Expand All @@ -20,9 +25,44 @@ public class MvcPartialJsonOptions
/// </summary>
public bool IgnoreParseErrors { get; set; } = false;

/// <summary>
/// Gets or sets a list of fields which should always be serialized.
/// </summary>
public IEnumerable<string> IgnoredFields
{
get
{
return this.ignoredFields;
}

set
{
this.ignoredFields = value;

this.ParsedIgnoredFields = this.ParseIgnoredFields(value);
}
}

/// <summary>
/// Gets the <see cref="JsonSerializerSettings"/> that are used by this application.
/// </summary>
public JsonSerializerSettings SerializerSettings { get; } = JsonSerializerSettingsProvider.CreateSerializerSettings();

internal Fields? ParsedIgnoredFields { get; private set; }

private Fields? ParseIgnoredFields(IEnumerable<string> value)
{
if (value != null)
{
if (!Fields.TryParse(string.Join(",", value), out var fields))
{
throw new ArgumentException($"Unable to parse {nameof(MvcPartialJsonOptions.IgnoredFields)} property.", nameof(value));
}

return fields;
}

return null;
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
using Newtonsoft.Json;
using PartialResponse.AspNetCore.Mvc.Formatters.Json;
using PartialResponse.AspNetCore.Mvc.Formatters.Json.Internal;
using PartialResponse.Core;

namespace PartialResponse.AspNetCore.Mvc.Formatters
{
Expand Down Expand Up @@ -176,13 +177,28 @@ private void WriteObject(TextWriter writer, object value, FieldsParserResult fie

if (fieldsParserResult.IsFieldsSet && !fieldsParserResult.HasError)
{
jsonSerializer.Serialize(jsonWriter, value, path => fieldsParserResult.Fields.Matches(path, this.options.IgnoreCase));
jsonSerializer.Serialize(jsonWriter, value, path => this.ShouldSerialize(path, fieldsParserResult.Fields, this.options));
}
else
{
jsonSerializer.Serialize(jsonWriter, value);
}
}
}

private bool ShouldSerialize(string path, Fields fields, MvcPartialJsonOptions options)
{
var parsedIgnoredFields = options.ParsedIgnoredFields;

if (parsedIgnoredFields.HasValue)
{
if (parsedIgnoredFields.Value.Matches(path, options.IgnoreCase))
{
return true;
}
}

return fields.Matches(path, options.IgnoreCase);
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
// Copyright (c) Arjen Post and contributors. See LICENSE and NOTICE in the project root for license information.

using System;
using Xunit;

namespace PartialResponse.AspNetCore.Mvc.Formatters.Json.Tests
{
public class MvcPartialJsonOptionsTests
{
[Fact]
public void TheIgnoredFieldsPropertyShouldThrowExceptionIfInvalid()
{
// Arrange
var options = new MvcPartialJsonOptions();

// Assert
Assert.Throws<ArgumentException>("value", () => options.IgnoredFields = new[] { "/foo" });
}

[Fact]
public void TheIgnoredFieldsPropertyShouldParseIgnoredFields()
{
// Arrange
var options = new MvcPartialJsonOptions();
var value = new[] { "foo" };

// Act
options.IgnoredFields = value;

// Assert
Assert.Same(value, options.IgnoredFields);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -200,6 +200,83 @@ public async Task TheWriteResponseBodyAsyncMethodShouldNotIgnoreCase()
Assert.Equal("{}", this.body.ToString());
}

[Fact]
public async Task TheWriteResponseBodyAsyncMethodShouldAlwaysSerializeIgnoredFields()
{
// Arrange
Mock.Get(this.httpResponse)
.SetupGet(httpResponse => httpResponse.StatusCode)
.Returns(200);

Fields.TryParse("foo", out var fields);

Mock.Get(this.fieldsParser)
.Setup(fieldsParser => fieldsParser.Parse(this.httpRequest))
.Returns(FieldsParserResult.Success(fields));

this.partialJsonOptions.IgnoredFields = new[] { "bar" };

var writeContext = this.CreateWriteContext(new { bar = "baz" });

// Act
await this.formatter.WriteResponseBodyAsync(writeContext, Encoding.UTF8);

// Assert
Assert.Equal("{\"bar\":\"baz\"}", this.body.ToString());
}

[Fact]
public async Task TheWriteResponseBodyAsyncMethodShouldAlwaysSerializeIgnoredFieldsIgnoringCase()
{
// Arrange
Mock.Get(this.httpResponse)
.SetupGet(httpResponse => httpResponse.StatusCode)
.Returns(200);

Fields.TryParse("foo", out var fields);

Mock.Get(this.fieldsParser)
.Setup(fieldsParser => fieldsParser.Parse(this.httpRequest))
.Returns(FieldsParserResult.Success(fields));

this.partialJsonOptions.IgnoreCase = true;
this.partialJsonOptions.IgnoredFields = new[] { "BAR" };

var writeContext = this.CreateWriteContext(new { bar = "baz" });

// Act
await this.formatter.WriteResponseBodyAsync(writeContext, Encoding.UTF8);

// Assert
Assert.Equal("{\"bar\":\"baz\"}", this.body.ToString());
}

[Fact]
public async Task TheWriteResponseBodyAsyncMethodShouldAlwaysSerializeIgnoredFieldsNotIgnoringCase()
{
// Arrange
Mock.Get(this.httpResponse)
.SetupGet(httpResponse => httpResponse.StatusCode)
.Returns(200);

Fields.TryParse("foo", out var fields);

Mock.Get(this.fieldsParser)
.Setup(fieldsParser => fieldsParser.Parse(this.httpRequest))
.Returns(FieldsParserResult.Success(fields));

this.partialJsonOptions.IgnoreCase = false;
this.partialJsonOptions.IgnoredFields = new[] { "BAR" };

var writeContext = this.CreateWriteContext(new { bar = "baz" });

// Act
await this.formatter.WriteResponseBodyAsync(writeContext, Encoding.UTF8);

// Assert
Assert.Equal("{}", this.body.ToString());
}

[Fact]
public async Task TheWriteResponseBodyAsyncMethodShouldBypassPartialResponseIfConfigured()
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -172,5 +172,70 @@ public async Task TheExecuteAsyncMethodShouldNotIgnoreCase()
// Assert
Assert.Equal("{}", this.body.ToString());
}

[Fact]
public async Task TheExecuteAsyncMethodShouldAlwaysSerializeIgnoredFields()
{
// Arrange
Fields.TryParse("foo", out var fields);

Mock.Get(this.fieldsParser)
.Setup(fieldsParser => fieldsParser.Parse(this.httpRequest))
.Returns(FieldsParserResult.Success(fields));

this.partialJsonOptions.IgnoredFields = new[] { "bar" };

var partialJsonResult = new PartialJsonResult(new { bar = "baz" }, new JsonSerializerSettings());

// Act
await this.executor.ExecuteAsync(this.actionContext, partialJsonResult);

// Assert
Assert.Equal("{\"bar\":\"baz\"}", this.body.ToString());
}

[Fact]
public async Task TheExecuteAsyncMethodShouldAlwaysSerializeIgnoredFieldsIgnoringCase()
{
// Arrange
Fields.TryParse("foo", out var fields);

Mock.Get(this.fieldsParser)
.Setup(fieldsParser => fieldsParser.Parse(this.httpRequest))
.Returns(FieldsParserResult.Success(fields));

this.partialJsonOptions.IgnoreCase = true;
this.partialJsonOptions.IgnoredFields = new[] { "BAR" };

var partialJsonResult = new PartialJsonResult(new { bar = "baz" }, new JsonSerializerSettings());

// Act
await this.executor.ExecuteAsync(this.actionContext, partialJsonResult);

// Assert
Assert.Equal("{\"bar\":\"baz\"}", this.body.ToString());
}

[Fact]
public async Task TheExecuteAsyncMethodShouldAlwaysSerializeIgnoredFieldsNotIgnoringCase()
{
// Arrange
Fields.TryParse("foo", out var fields);

Mock.Get(this.fieldsParser)
.Setup(fieldsParser => fieldsParser.Parse(this.httpRequest))
.Returns(FieldsParserResult.Success(fields));

this.partialJsonOptions.IgnoreCase = false;
this.partialJsonOptions.IgnoredFields = new[] { "BAR" };

var partialJsonResult = new PartialJsonResult(new { bar = "baz" }, new JsonSerializerSettings());

// Act
await this.executor.ExecuteAsync(this.actionContext, partialJsonResult);

// Assert
Assert.Equal("{}", this.body.ToString());
}
}
}