Add a System.Text.Json based IJsonHelper (#9566)
* Add a System.Text.Json based IJsonHelper Fixes https://github.com/aspnet/AspNetCore/issues/9564
This commit is contained in:
parent
c300c8c97c
commit
e42f979cca
|
|
@ -36,7 +36,7 @@ namespace Microsoft.Extensions.DependencyInjection
|
||||||
{
|
{
|
||||||
// Arrange
|
// Arrange
|
||||||
var services = new ServiceCollection()
|
var services = new ServiceCollection()
|
||||||
.AddSingleton<IJsonHelper, DefaultJsonHelper>();
|
.AddSingleton<IJsonHelper, SystemTextJsonHelper>();
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
NewtonsoftJsonMvcCoreBuilderExtensions.AddServicesCore(services);
|
NewtonsoftJsonMvcCoreBuilderExtensions.AddServicesCore(services);
|
||||||
|
|
|
||||||
|
|
@ -16,6 +16,7 @@
|
||||||
<Compile Include="..\..\Mvc.Core\test\Formatters\JsonOutputFormatterTestBase.cs" />
|
<Compile Include="..\..\Mvc.Core\test\Formatters\JsonOutputFormatterTestBase.cs" />
|
||||||
<Compile Include="..\..\Mvc.Core\test\Infrastructure\JsonResultExecutorTestBase.cs" />
|
<Compile Include="..\..\Mvc.Core\test\Infrastructure\JsonResultExecutorTestBase.cs" />
|
||||||
<Compile Include="..\..\Mvc.ViewFeatures\test\Infrastructure\TempDataSerializerTestBase.cs" />
|
<Compile Include="..\..\Mvc.ViewFeatures\test\Infrastructure\TempDataSerializerTestBase.cs" />
|
||||||
|
<Compile Include="..\..\Mvc.ViewFeatures\test\Rendering\JsonHelperTestBase.cs" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
</Project>
|
</Project>
|
||||||
|
|
|
||||||
|
|
@ -3,6 +3,7 @@
|
||||||
|
|
||||||
using System.Buffers;
|
using System.Buffers;
|
||||||
using Microsoft.AspNetCore.Html;
|
using Microsoft.AspNetCore.Html;
|
||||||
|
using Microsoft.AspNetCore.Mvc.Rendering;
|
||||||
using Microsoft.Extensions.Options;
|
using Microsoft.Extensions.Options;
|
||||||
using Newtonsoft.Json;
|
using Newtonsoft.Json;
|
||||||
using Newtonsoft.Json.Serialization;
|
using Newtonsoft.Json.Serialization;
|
||||||
|
|
@ -10,31 +11,8 @@ using Xunit;
|
||||||
|
|
||||||
namespace Microsoft.AspNetCore.Mvc.NewtonsoftJson
|
namespace Microsoft.AspNetCore.Mvc.NewtonsoftJson
|
||||||
{
|
{
|
||||||
public class NewtonsoftJsonHelperTest
|
public class NewtonsoftJsonHelperTest : JsonHelperTestBase
|
||||||
{
|
{
|
||||||
[Fact]
|
|
||||||
public void Serialize_EscapesHtmlByDefault()
|
|
||||||
{
|
|
||||||
// Arrange
|
|
||||||
var options = new MvcNewtonsoftJsonOptions();
|
|
||||||
options.SerializerSettings.StringEscapeHandling = StringEscapeHandling.EscapeNonAscii;
|
|
||||||
var helper = new NewtonsoftJsonHelper(
|
|
||||||
Options.Create(options),
|
|
||||||
ArrayPool<char>.Shared);
|
|
||||||
var obj = new
|
|
||||||
{
|
|
||||||
HTML = "<b>John Doe</b>"
|
|
||||||
};
|
|
||||||
var expectedOutput = "{\"html\":\"\\u003cb\\u003eJohn Doe\\u003c/b\\u003e\"}";
|
|
||||||
|
|
||||||
// Act
|
|
||||||
var result = helper.Serialize(obj);
|
|
||||||
|
|
||||||
// Assert
|
|
||||||
var htmlString = Assert.IsType<HtmlString>(result);
|
|
||||||
Assert.Equal(expectedOutput, htmlString.ToString());
|
|
||||||
}
|
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public void Serialize_MaintainsSettingsAndEscapesHtml()
|
public void Serialize_MaintainsSettingsAndEscapesHtml()
|
||||||
{
|
{
|
||||||
|
|
@ -65,10 +43,7 @@ namespace Microsoft.AspNetCore.Mvc.NewtonsoftJson
|
||||||
public void Serialize_UsesHtmlSafeVersionOfPassedInSettings()
|
public void Serialize_UsesHtmlSafeVersionOfPassedInSettings()
|
||||||
{
|
{
|
||||||
// Arrange
|
// Arrange
|
||||||
var options = new MvcNewtonsoftJsonOptions();
|
var helper = GetJsonHelper();
|
||||||
var helper = new NewtonsoftJsonHelper(
|
|
||||||
Options.Create(options),
|
|
||||||
ArrayPool<char>.Shared);
|
|
||||||
var obj = new
|
var obj = new
|
||||||
{
|
{
|
||||||
FullHtml = "<b>John Doe</b>"
|
FullHtml = "<b>John Doe</b>"
|
||||||
|
|
@ -91,5 +66,33 @@ namespace Microsoft.AspNetCore.Mvc.NewtonsoftJson
|
||||||
var htmlString = Assert.IsType<HtmlString>(result);
|
var htmlString = Assert.IsType<HtmlString>(result);
|
||||||
Assert.Equal(expectedOutput, htmlString.ToString());
|
Assert.Equal(expectedOutput, htmlString.ToString());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public override void Serialize_WithNonAsciiChars()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var helper = GetJsonHelper();
|
||||||
|
var obj = new
|
||||||
|
{
|
||||||
|
HTML = $"Hello pingüino"
|
||||||
|
};
|
||||||
|
var expectedOutput = $"{{\"html\":\"{obj.HTML}\"}}";
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var result = helper.Serialize(obj);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
var htmlString = Assert.IsType<HtmlString>(result);
|
||||||
|
Assert.Equal(expectedOutput, htmlString.ToString());
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override IJsonHelper GetJsonHelper()
|
||||||
|
{
|
||||||
|
var options = new MvcNewtonsoftJsonOptions();
|
||||||
|
var helper = new NewtonsoftJsonHelper(
|
||||||
|
Options.Create(options),
|
||||||
|
ArrayPool<char>.Shared);
|
||||||
|
return helper;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -172,7 +172,7 @@ namespace Microsoft.Extensions.DependencyInjection
|
||||||
services.TryAddSingleton<IModelExpressionProvider>(s => s.GetRequiredService<ModelExpressionProvider>());
|
services.TryAddSingleton<IModelExpressionProvider>(s => s.GetRequiredService<ModelExpressionProvider>());
|
||||||
services.TryAddSingleton<ValidationHtmlAttributeProvider, DefaultValidationHtmlAttributeProvider>();
|
services.TryAddSingleton<ValidationHtmlAttributeProvider, DefaultValidationHtmlAttributeProvider>();
|
||||||
|
|
||||||
services.TryAddSingleton<IJsonHelper, DefaultJsonHelper>();
|
services.TryAddSingleton<IJsonHelper, SystemTextJsonHelper>();
|
||||||
|
|
||||||
//
|
//
|
||||||
// View Components
|
// View Components
|
||||||
|
|
|
||||||
|
|
@ -1,24 +0,0 @@
|
||||||
|
|
||||||
// Copyright (c) .NET Foundation. All rights reserved.
|
|
||||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
|
||||||
|
|
||||||
using System;
|
|
||||||
using Microsoft.AspNetCore.Html;
|
|
||||||
using Microsoft.Extensions.DependencyInjection;
|
|
||||||
|
|
||||||
namespace Microsoft.AspNetCore.Mvc.Rendering
|
|
||||||
{
|
|
||||||
internal class DefaultJsonHelper : IJsonHelper
|
|
||||||
{
|
|
||||||
/// <inheritdoc />
|
|
||||||
public IHtmlContent Serialize(object value)
|
|
||||||
{
|
|
||||||
throw new InvalidOperationException(Core.Resources.FormatReferenceToNewtonsoftJsonRequired(
|
|
||||||
$"{nameof(IJsonHelper)}.{nameof(Serialize)}",
|
|
||||||
"Microsoft.AspNetCore.Mvc.NewtonsoftJson",
|
|
||||||
nameof(IMvcBuilder),
|
|
||||||
"AddNewtonsoftJson",
|
|
||||||
"ConfigureServices(...)"));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -0,0 +1,29 @@
|
||||||
|
|
||||||
|
// Copyright (c) .NET Foundation. All rights reserved.
|
||||||
|
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||||
|
|
||||||
|
using System.Text.Json.Serialization;
|
||||||
|
using Microsoft.AspNetCore.Html;
|
||||||
|
using Microsoft.Extensions.Options;
|
||||||
|
|
||||||
|
namespace Microsoft.AspNetCore.Mvc.Rendering
|
||||||
|
{
|
||||||
|
internal class SystemTextJsonHelper : IJsonHelper
|
||||||
|
{
|
||||||
|
private readonly MvcOptions _mvcOptions;
|
||||||
|
|
||||||
|
public SystemTextJsonHelper(IOptions<MvcOptions> mvcOptions)
|
||||||
|
{
|
||||||
|
_mvcOptions = mvcOptions.Value;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public IHtmlContent Serialize(object value)
|
||||||
|
{
|
||||||
|
// JsonSerializer always encodes non-ASCII chars, so we do not need
|
||||||
|
// to do anything special with the SerializerOptions
|
||||||
|
var json = JsonSerializer.ToString(value, _mvcOptions.SerializerOptions);
|
||||||
|
return new HtmlString(json);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,103 @@
|
||||||
|
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||||
|
|
||||||
|
using Microsoft.AspNetCore.Html;
|
||||||
|
using Xunit;
|
||||||
|
|
||||||
|
namespace Microsoft.AspNetCore.Mvc.Rendering
|
||||||
|
{
|
||||||
|
public abstract class JsonHelperTestBase
|
||||||
|
{
|
||||||
|
protected abstract IJsonHelper GetJsonHelper();
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void Serialize_EscapesHtmlByDefault()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var helper = GetJsonHelper();
|
||||||
|
var obj = new
|
||||||
|
{
|
||||||
|
HTML = "<b>John Doe</b>"
|
||||||
|
};
|
||||||
|
var expectedOutput = "{\"html\":\"\\u003cb\\u003eJohn Doe\\u003c/b\\u003e\"}";
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var result = helper.Serialize(obj);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
var htmlString = Assert.IsType<HtmlString>(result);
|
||||||
|
Assert.Equal(expectedOutput, htmlString.ToString());
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void Serialize_WithNullValue()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var helper = GetJsonHelper();
|
||||||
|
var expectedOutput = "null";
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var result = helper.Serialize(value: null);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
var htmlString = Assert.IsType<HtmlString>(result);
|
||||||
|
Assert.Equal(expectedOutput, htmlString.ToString());
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void Serialize_WithControlCharacters()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var helper = GetJsonHelper();
|
||||||
|
var obj = new
|
||||||
|
{
|
||||||
|
HTML = $"Hello \n \b \a world"
|
||||||
|
};
|
||||||
|
var expectedOutput = "{\"html\":\"Hello \\n \\b \\u0007 world\"}";
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var result = helper.Serialize(obj);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
var htmlString = Assert.IsType<HtmlString>(result);
|
||||||
|
Assert.Equal(expectedOutput, htmlString.ToString());
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public virtual void Serialize_WithNonAsciiChars()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var helper = GetJsonHelper();
|
||||||
|
var obj = new
|
||||||
|
{
|
||||||
|
HTML = $"Hello pingüino"
|
||||||
|
};
|
||||||
|
var expectedOutput = "{\"html\":\"Hello ping\\u00fcino\"}";
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var result = helper.Serialize(obj);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
var htmlString = Assert.IsType<HtmlString>(result);
|
||||||
|
Assert.Equal(expectedOutput, htmlString.ToString());
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void Serialize_WithHTMLEntities()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var helper = GetJsonHelper();
|
||||||
|
var obj = new
|
||||||
|
{
|
||||||
|
HTML = $"Hello <John>"
|
||||||
|
};
|
||||||
|
var expectedOutput = "{\"html\":\"Hello \\u0026nbsp; \\u0026lt;John\\u0026gt;\"}";
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var result = helper.Serialize(obj);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
var htmlString = Assert.IsType<HtmlString>(result);
|
||||||
|
Assert.Equal(expectedOutput, htmlString.ToString());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,19 @@
|
||||||
|
|
||||||
|
// Copyright (c) .NET Foundation. All rights reserved.
|
||||||
|
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||||
|
|
||||||
|
|
||||||
|
using System.Text.Json.Serialization;
|
||||||
|
using Microsoft.Extensions.Options;
|
||||||
|
|
||||||
|
namespace Microsoft.AspNetCore.Mvc.Rendering
|
||||||
|
{
|
||||||
|
public class SystemTextJsonHelperTest : JsonHelperTestBase
|
||||||
|
{
|
||||||
|
protected override IJsonHelper GetJsonHelper()
|
||||||
|
{
|
||||||
|
var mvcOptions = new MvcOptions { SerializerOptions = { PropertyNamingPolicy = JsonNamingPolicy.CamelCase } };
|
||||||
|
return new SystemTextJsonHelper(Options.Create(mvcOptions));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
Loading…
Reference in New Issue