Ensure SystemTextJsonHelper always HTML encodes output. (#12808)
* Ensure JsonSerializer always HTML encodes output. * Update JsonOptions.JsonSerializerOptions to use encoder scheme that does not encode non-ASCII characters by default. This makes the encoding comparable to Json.NET's defaults * In SystemTextJsonHelper, ensure that the content is always HTML-encoded * Unskip skipped test Fixes https://github.com/aspnet/AspNetCore/issues/9946 Fixes https://github.com/aspnet/AspNetCore/issues/11459
This commit is contained in:
parent
709b390157
commit
397f924e8d
|
|
@ -2210,7 +2210,7 @@ namespace Microsoft.AspNetCore.Mvc.Formatters
|
|||
}
|
||||
public partial class SystemTextJsonOutputFormatter : Microsoft.AspNetCore.Mvc.Formatters.TextOutputFormatter
|
||||
{
|
||||
public SystemTextJsonOutputFormatter(Microsoft.AspNetCore.Mvc.JsonOptions options) { }
|
||||
public SystemTextJsonOutputFormatter(System.Text.Json.JsonSerializerOptions jsonSerializerOptions) { }
|
||||
public System.Text.Json.JsonSerializerOptions SerializerOptions { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } }
|
||||
[System.Diagnostics.DebuggerStepThroughAttribute]
|
||||
public sealed override System.Threading.Tasks.Task WriteResponseBodyAsync(Microsoft.AspNetCore.Mvc.Formatters.OutputFormatterWriteContext context, System.Text.Encoding selectedEncoding) { throw null; }
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@
|
|||
using System;
|
||||
using System.IO;
|
||||
using System.Text;
|
||||
using System.Text.Encodings.Web;
|
||||
using System.Text.Json;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
|
@ -17,14 +18,13 @@ namespace Microsoft.AspNetCore.Mvc.Formatters
|
|||
/// </summary>
|
||||
public class SystemTextJsonOutputFormatter : TextOutputFormatter
|
||||
{
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new <see cref="SystemTextJsonOutputFormatter"/> instance.
|
||||
/// </summary>
|
||||
/// <param name="options">The <see cref="JsonOptions"/>.</param>
|
||||
public SystemTextJsonOutputFormatter(JsonOptions options)
|
||||
/// <param name="jsonSerializerOptions">The <see cref="JsonSerializerOptions"/>.</param>
|
||||
public SystemTextJsonOutputFormatter(JsonSerializerOptions jsonSerializerOptions)
|
||||
{
|
||||
SerializerOptions = options.JsonSerializerOptions;
|
||||
SerializerOptions = jsonSerializerOptions;
|
||||
|
||||
SupportedEncodings.Add(Encoding.UTF8);
|
||||
SupportedEncodings.Add(Encoding.Unicode);
|
||||
|
|
@ -33,6 +33,19 @@ namespace Microsoft.AspNetCore.Mvc.Formatters
|
|||
SupportedMediaTypes.Add(MediaTypeHeaderValues.ApplicationAnyJsonSyntax);
|
||||
}
|
||||
|
||||
internal static SystemTextJsonOutputFormatter CreateFormatter(JsonOptions jsonOptions)
|
||||
{
|
||||
var jsonSerializerOptions = jsonOptions.JsonSerializerOptions;
|
||||
|
||||
if (jsonSerializerOptions.Encoder is null)
|
||||
{
|
||||
// If the user hasn't explicitly configured the encoder, use the less strict encoder that does not encode all non-ASCII characters.
|
||||
jsonSerializerOptions = jsonSerializerOptions.Copy(JavaScriptEncoder.UnsafeRelaxedJsonEscaping);
|
||||
}
|
||||
|
||||
return new SystemTextJsonOutputFormatter(jsonSerializerOptions);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the <see cref="JsonSerializerOptions"/> used to configure the <see cref="JsonSerializer"/>.
|
||||
/// </summary>
|
||||
|
|
|
|||
|
|
@ -0,0 +1,36 @@
|
|||
// 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.Encodings.Web;
|
||||
|
||||
namespace System.Text.Json
|
||||
{
|
||||
internal static class JsonSerializerOptionsCopyConstructor
|
||||
{
|
||||
public static JsonSerializerOptions Copy(this JsonSerializerOptions serializerOptions, JavaScriptEncoder encoder)
|
||||
{
|
||||
var copiedOptions = new JsonSerializerOptions
|
||||
{
|
||||
AllowTrailingCommas = serializerOptions.AllowTrailingCommas,
|
||||
DefaultBufferSize = serializerOptions.DefaultBufferSize,
|
||||
DictionaryKeyPolicy = serializerOptions.DictionaryKeyPolicy,
|
||||
IgnoreNullValues = serializerOptions.IgnoreNullValues,
|
||||
IgnoreReadOnlyProperties = serializerOptions.IgnoreReadOnlyProperties,
|
||||
MaxDepth = serializerOptions.MaxDepth,
|
||||
PropertyNameCaseInsensitive = serializerOptions.PropertyNameCaseInsensitive,
|
||||
PropertyNamingPolicy = serializerOptions.PropertyNamingPolicy,
|
||||
ReadCommentHandling = serializerOptions.ReadCommentHandling,
|
||||
WriteIndented = serializerOptions.WriteIndented
|
||||
};
|
||||
|
||||
for (var i = 0; i < serializerOptions.Converters.Count; i++)
|
||||
{
|
||||
copiedOptions.Converters.Add(serializerOptions.Converters[i]);
|
||||
}
|
||||
|
||||
copiedOptions.Encoder = encoder;
|
||||
|
||||
return copiedOptions;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -87,7 +87,9 @@ namespace Microsoft.AspNetCore.Mvc
|
|||
options.OutputFormatters.Add(new HttpNoContentOutputFormatter());
|
||||
options.OutputFormatters.Add(new StringOutputFormatter());
|
||||
options.OutputFormatters.Add(new StreamOutputFormatter());
|
||||
options.OutputFormatters.Add(new SystemTextJsonOutputFormatter(_jsonOptions.Value));
|
||||
|
||||
var jsonOutputFormatter = SystemTextJsonOutputFormatter.CreateFormatter(_jsonOptions.Value);
|
||||
options.OutputFormatters.Add(jsonOutputFormatter);
|
||||
|
||||
// Set up ValueProviders
|
||||
options.ValueProviderFactories.Add(new FormValueProviderFactory());
|
||||
|
|
|
|||
|
|
@ -91,7 +91,7 @@ namespace Microsoft.AspNetCore.Mvc
|
|||
{
|
||||
var options = Options.Create(new MvcOptions());
|
||||
options.Value.OutputFormatters.Add(new StringOutputFormatter());
|
||||
options.Value.OutputFormatters.Add(new SystemTextJsonOutputFormatter(new JsonOptions()));
|
||||
options.Value.OutputFormatters.Add(SystemTextJsonOutputFormatter.CreateFormatter(new JsonOptions()));
|
||||
|
||||
var services = new ServiceCollection();
|
||||
services.AddSingleton<IActionResultExecutor<ObjectResult>>(new ObjectResultExecutor(
|
||||
|
|
|
|||
|
|
@ -104,7 +104,7 @@ namespace Microsoft.AspNetCore.Mvc
|
|||
{
|
||||
var options = Options.Create(new MvcOptions());
|
||||
options.Value.OutputFormatters.Add(new StringOutputFormatter());
|
||||
options.Value.OutputFormatters.Add(new SystemTextJsonOutputFormatter(new JsonOptions()));
|
||||
options.Value.OutputFormatters.Add(SystemTextJsonOutputFormatter.CreateFormatter(new JsonOptions()));
|
||||
|
||||
var services = new ServiceCollection();
|
||||
services.AddSingleton<IActionResultExecutor<ObjectResult>>(new ObjectResultExecutor(
|
||||
|
|
|
|||
|
|
@ -92,7 +92,7 @@ namespace Microsoft.AspNetCore.Mvc
|
|||
{
|
||||
var options = Options.Create(new MvcOptions());
|
||||
options.Value.OutputFormatters.Add(new StringOutputFormatter());
|
||||
options.Value.OutputFormatters.Add(new SystemTextJsonOutputFormatter(new JsonOptions()));
|
||||
options.Value.OutputFormatters.Add(SystemTextJsonOutputFormatter.CreateFormatter(new JsonOptions()));
|
||||
|
||||
var services = new ServiceCollection();
|
||||
services.AddSingleton<IActionResultExecutor<ObjectResult>>(new ObjectResultExecutor(
|
||||
|
|
|
|||
|
|
@ -465,7 +465,7 @@ namespace Microsoft.AspNetCore.Mvc.Formatters
|
|||
// Set up default output formatters.
|
||||
MvcOptions.OutputFormatters.Add(new HttpNoContentOutputFormatter());
|
||||
MvcOptions.OutputFormatters.Add(new StringOutputFormatter());
|
||||
MvcOptions.OutputFormatters.Add(new SystemTextJsonOutputFormatter(new JsonOptions()));
|
||||
MvcOptions.OutputFormatters.Add(SystemTextJsonOutputFormatter.CreateFormatter(new JsonOptions()));
|
||||
|
||||
// Set up default mapping for json extensions to content type
|
||||
MvcOptions.FormatterMappings.SetMediaTypeMappingForFormat(
|
||||
|
|
|
|||
|
|
@ -76,6 +76,71 @@ namespace Microsoft.AspNetCore.Mvc.Formatters
|
|||
}
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[MemberData(nameof(WriteCorrectCharacterEncoding))]
|
||||
public async Task WriteToStreamAsync_UsesCorrectCharacterEncoding(
|
||||
string content,
|
||||
string encodingAsString,
|
||||
bool isDefaultEncoding)
|
||||
{
|
||||
// Arrange
|
||||
var formatter = GetOutputFormatter();
|
||||
var expectedContent = "\"" + content + "\"";
|
||||
var mediaType = MediaTypeHeaderValue.Parse(string.Format("application/json; charset={0}", encodingAsString));
|
||||
var encoding = CreateOrGetSupportedEncoding(formatter, encodingAsString, isDefaultEncoding);
|
||||
|
||||
|
||||
var body = new MemoryStream();
|
||||
var actionContext = GetActionContext(mediaType, body);
|
||||
|
||||
var outputFormatterContext = new OutputFormatterWriteContext(
|
||||
actionContext.HttpContext,
|
||||
new TestHttpResponseStreamWriterFactory().CreateWriter,
|
||||
typeof(string),
|
||||
content)
|
||||
{
|
||||
ContentType = new StringSegment(mediaType.ToString()),
|
||||
};
|
||||
|
||||
// Act
|
||||
await formatter.WriteResponseBodyAsync(outputFormatterContext, Encoding.GetEncoding(encodingAsString));
|
||||
|
||||
// Assert
|
||||
var actualContent = encoding.GetString(body.ToArray());
|
||||
Assert.Equal(expectedContent, actualContent, StringComparer.OrdinalIgnoreCase);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task WriteResponseBodyAsync_Encodes()
|
||||
{
|
||||
// Arrange
|
||||
var formatter = GetOutputFormatter();
|
||||
var expectedContent = "{\"key\":\"Hello \\n <b>Wörld</b>\"}";
|
||||
var content = new { key = "Hello \n <b>Wörld</b>" };
|
||||
|
||||
var mediaType = MediaTypeHeaderValue.Parse("application/json; charset=utf-8");
|
||||
var encoding = CreateOrGetSupportedEncoding(formatter, "utf-8", isDefaultEncoding: true);
|
||||
|
||||
var body = new MemoryStream();
|
||||
var actionContext = GetActionContext(mediaType, body);
|
||||
|
||||
var outputFormatterContext = new OutputFormatterWriteContext(
|
||||
actionContext.HttpContext,
|
||||
new TestHttpResponseStreamWriterFactory().CreateWriter,
|
||||
typeof(string),
|
||||
content)
|
||||
{
|
||||
ContentType = new StringSegment(mediaType.ToString()),
|
||||
};
|
||||
|
||||
// Act
|
||||
await formatter.WriteResponseBodyAsync(outputFormatterContext, Encoding.GetEncoding("utf-8"));
|
||||
|
||||
// Assert
|
||||
var actualContent = encoding.GetString(body.ToArray());
|
||||
Assert.Equal(expectedContent, actualContent);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task ErrorDuringSerialization_DoesNotCloseTheBrackets()
|
||||
{
|
||||
|
|
|
|||
|
|
@ -1,56 +1,13 @@
|
|||
// 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 System.IO;
|
||||
using System.Text;
|
||||
using System.Text.Encodings.Web;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.Extensions.Primitives;
|
||||
using Microsoft.Net.Http.Headers;
|
||||
using Xunit;
|
||||
|
||||
namespace Microsoft.AspNetCore.Mvc.Formatters
|
||||
{
|
||||
public class SystemTextJsonOutputFormatterTest : JsonOutputFormatterTestBase
|
||||
{
|
||||
protected override TextOutputFormatter GetOutputFormatter()
|
||||
{
|
||||
return new SystemTextJsonOutputFormatter(new JsonOptions());
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[MemberData(nameof(WriteCorrectCharacterEncoding))]
|
||||
public async Task WriteToStreamAsync_UsesCorrectCharacterEncoding(
|
||||
string content,
|
||||
string encodingAsString,
|
||||
bool isDefaultEncoding)
|
||||
{
|
||||
// Arrange
|
||||
var formatter = GetOutputFormatter();
|
||||
var expectedContent = "\"" + JavaScriptEncoder.Default.Encode(content) + "\"";
|
||||
var mediaType = MediaTypeHeaderValue.Parse(string.Format("application/json; charset={0}", encodingAsString));
|
||||
var encoding = CreateOrGetSupportedEncoding(formatter, encodingAsString, isDefaultEncoding);
|
||||
|
||||
|
||||
var body = new MemoryStream();
|
||||
var actionContext = GetActionContext(mediaType, body);
|
||||
|
||||
var outputFormatterContext = new OutputFormatterWriteContext(
|
||||
actionContext.HttpContext,
|
||||
new TestHttpResponseStreamWriterFactory().CreateWriter,
|
||||
typeof(string),
|
||||
content)
|
||||
{
|
||||
ContentType = new StringSegment(mediaType.ToString()),
|
||||
};
|
||||
|
||||
// Act
|
||||
await formatter.WriteResponseBodyAsync(outputFormatterContext, Encoding.GetEncoding(encodingAsString));
|
||||
|
||||
// Assert
|
||||
var actualContent = encoding.GetString(body.ToArray());
|
||||
Assert.Equal(expectedContent, actualContent, StringComparer.OrdinalIgnoreCase);
|
||||
return SystemTextJsonOutputFormatter.CreateFormatter(new JsonOptions());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -69,7 +69,7 @@ namespace Microsoft.AspNetCore.Mvc
|
|||
{
|
||||
var options = Options.Create(new MvcOptions());
|
||||
options.Value.OutputFormatters.Add(new StringOutputFormatter());
|
||||
options.Value.OutputFormatters.Add(new SystemTextJsonOutputFormatter(new JsonOptions()));
|
||||
options.Value.OutputFormatters.Add(SystemTextJsonOutputFormatter.CreateFormatter(new JsonOptions()));
|
||||
|
||||
var services = new ServiceCollection();
|
||||
services.AddSingleton<IActionResultExecutor<ObjectResult>>(new ObjectResultExecutor(
|
||||
|
|
|
|||
|
|
@ -70,7 +70,7 @@ namespace Microsoft.AspNetCore.Mvc
|
|||
{
|
||||
var options = Options.Create(new MvcOptions());
|
||||
options.Value.OutputFormatters.Add(new StringOutputFormatter());
|
||||
options.Value.OutputFormatters.Add(new SystemTextJsonOutputFormatter(new JsonOptions()));
|
||||
options.Value.OutputFormatters.Add(SystemTextJsonOutputFormatter.CreateFormatter(new JsonOptions()));
|
||||
|
||||
var services = new ServiceCollection();
|
||||
services.AddSingleton<IActionResultExecutor<ObjectResult>>(new ObjectResultExecutor(
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@
|
|||
// 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.Encodings.Web;
|
||||
using System.Text.Json;
|
||||
using Microsoft.AspNetCore.Html;
|
||||
using Microsoft.Extensions.Options;
|
||||
|
|
@ -10,11 +11,11 @@ namespace Microsoft.AspNetCore.Mvc.Rendering
|
|||
{
|
||||
internal class SystemTextJsonHelper : IJsonHelper
|
||||
{
|
||||
private readonly JsonOptions _options;
|
||||
private readonly JsonSerializerOptions _htmlSafeJsonSerializerOptions;
|
||||
|
||||
public SystemTextJsonHelper(IOptions<JsonOptions> options)
|
||||
{
|
||||
_options = options.Value;
|
||||
_htmlSafeJsonSerializerOptions = GetHtmlSafeSerializerOptions(options.Value.JsonSerializerOptions);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
|
|
@ -22,8 +23,18 @@ namespace Microsoft.AspNetCore.Mvc.Rendering
|
|||
{
|
||||
// JsonSerializer always encodes non-ASCII chars, so we do not need
|
||||
// to do anything special with the SerializerOptions
|
||||
var json = JsonSerializer.Serialize(value, _options.JsonSerializerOptions);
|
||||
var json = JsonSerializer.Serialize(value, _htmlSafeJsonSerializerOptions);
|
||||
return new HtmlString(json);
|
||||
}
|
||||
|
||||
private static JsonSerializerOptions GetHtmlSafeSerializerOptions(JsonSerializerOptions serializerOptions)
|
||||
{
|
||||
if (serializerOptions.Encoder is null || serializerOptions.Encoder == JavaScriptEncoder.Default)
|
||||
{
|
||||
return serializerOptions;
|
||||
}
|
||||
|
||||
return serializerOptions.Copy(JavaScriptEncoder.Default);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -25,7 +25,7 @@ namespace Microsoft.AspNetCore.Mvc.Rendering
|
|||
|
||||
// Assert
|
||||
var htmlString = Assert.IsType<HtmlString>(result);
|
||||
Assert.Equal(expectedOutput, htmlString.ToString());
|
||||
Assert.Equal(expectedOutput, htmlString.ToString(), ignoreCase: true);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
|
|
@ -71,14 +71,14 @@ namespace Microsoft.AspNetCore.Mvc.Rendering
|
|||
{
|
||||
HTML = $"Hello pingüino"
|
||||
};
|
||||
var expectedOutput = "{\"html\":\"Hello ping\\u00fcino\"}";
|
||||
var expectedOutput = "{\"html\":\"Hello pingüino\"}";
|
||||
|
||||
// Act
|
||||
var result = helper.Serialize(obj);
|
||||
|
||||
// Assert
|
||||
var htmlString = Assert.IsType<HtmlString>(result);
|
||||
Assert.Equal(expectedOutput, htmlString.ToString());
|
||||
Assert.Equal(expectedOutput, htmlString.ToString(), ignoreCase: true);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
|
|
@ -99,5 +99,25 @@ namespace Microsoft.AspNetCore.Mvc.Rendering
|
|||
var htmlString = Assert.IsType<HtmlString>(result);
|
||||
Assert.Equal(expectedOutput, htmlString.ToString());
|
||||
}
|
||||
|
||||
|
||||
[Fact]
|
||||
public virtual void Serialize_WithHTMLNonAsciiAndControlChars()
|
||||
{
|
||||
// Arrange
|
||||
var helper = GetJsonHelper();
|
||||
var obj = new
|
||||
{
|
||||
HTML = "<b>Hello \n pingüino</b>"
|
||||
};
|
||||
var expectedOutput = "{\"html\":\"\\u003cb\\u003eHello \\n pingüino\\u003c/b\\u003e\"}";
|
||||
|
||||
// Act
|
||||
var result = helper.Serialize(obj);
|
||||
|
||||
// Assert
|
||||
var htmlString = Assert.IsType<HtmlString>(result);
|
||||
Assert.Equal(expectedOutput, htmlString.ToString());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
// 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;
|
||||
using System.Text.Encodings.Web;
|
||||
using Microsoft.AspNetCore.Html;
|
||||
using Microsoft.Extensions.Options;
|
||||
using Xunit;
|
||||
|
|
@ -10,31 +10,13 @@ namespace Microsoft.AspNetCore.Mvc.Rendering
|
|||
{
|
||||
public class SystemTextJsonHelperTest : JsonHelperTestBase
|
||||
{
|
||||
protected override IJsonHelper GetJsonHelper()
|
||||
protected override IJsonHelper GetJsonHelper() => GetJsonHelper(new JsonOptions());
|
||||
|
||||
private static IJsonHelper GetJsonHelper(JsonOptions options)
|
||||
{
|
||||
var options = new JsonOptions() { JsonSerializerOptions = { PropertyNamingPolicy = JsonNamingPolicy.CamelCase } };
|
||||
return new SystemTextJsonHelper(Options.Create(options));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public override 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 override void Serialize_WithNonAsciiChars()
|
||||
{
|
||||
|
|
@ -53,5 +35,56 @@ namespace Microsoft.AspNetCore.Mvc.Rendering
|
|||
var htmlString = Assert.IsType<HtmlString>(result);
|
||||
Assert.Equal(expectedOutput, htmlString.ToString());
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public override void Serialize_WithHTMLNonAsciiAndControlChars()
|
||||
{
|
||||
// Arrange
|
||||
var helper = GetJsonHelper();
|
||||
var obj = new
|
||||
{
|
||||
HTML = "<b>Hello \n pingüino</b>"
|
||||
};
|
||||
var expectedOutput = "{\"html\":\"\\u003Cb\\u003EHello \\n ping\\u00FCino\\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_UsesOptionsConfiguredInTheProvider()
|
||||
{
|
||||
// Arrange
|
||||
// This should use property-casing and indentation, but the result should be HTML-safe
|
||||
var options = new JsonOptions
|
||||
{
|
||||
JsonSerializerOptions =
|
||||
{
|
||||
Encoder = JavaScriptEncoder.UnsafeRelaxedJsonEscaping,
|
||||
PropertyNamingPolicy = null,
|
||||
WriteIndented = true,
|
||||
}
|
||||
};
|
||||
var helper = GetJsonHelper(options);
|
||||
var obj = new
|
||||
{
|
||||
HTML = "<b>John</b>"
|
||||
};
|
||||
var expectedOutput =
|
||||
@"{
|
||||
""HTML"": ""\u003Cb\u003EJohn\u003C/b\u003E""
|
||||
}";
|
||||
|
||||
// Act
|
||||
var result = helper.Serialize(obj);
|
||||
|
||||
// Assert
|
||||
var htmlString = Assert.IsType<HtmlString>(result);
|
||||
Assert.Equal(expectedOutput, htmlString.ToString());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,7 +2,6 @@
|
|||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Linq;
|
||||
using System.Net;
|
||||
using System.Net.Http;
|
||||
|
|
@ -11,6 +10,7 @@ using System.Text;
|
|||
using System.Threading.Tasks;
|
||||
using FormatterWebSite.Controllers;
|
||||
using Microsoft.AspNetCore.Hosting;
|
||||
using Microsoft.AspNetCore.Mvc.Testing;
|
||||
using Newtonsoft.Json;
|
||||
using Newtonsoft.Json.Linq;
|
||||
using Xunit;
|
||||
|
|
@ -21,13 +21,14 @@ namespace Microsoft.AspNetCore.Mvc.FunctionalTests
|
|||
{
|
||||
protected JsonOutputFormatterTestBase(MvcTestFixture<TStartup> fixture)
|
||||
{
|
||||
var factory = fixture.Factories.FirstOrDefault() ?? fixture.WithWebHostBuilder(ConfigureWebHostBuilder);
|
||||
Client = factory.CreateDefaultClient();
|
||||
Factory = fixture.Factories.FirstOrDefault() ?? fixture.WithWebHostBuilder(ConfigureWebHostBuilder);
|
||||
Client = Factory.CreateDefaultClient();
|
||||
}
|
||||
|
||||
private static void ConfigureWebHostBuilder(IWebHostBuilder builder) =>
|
||||
builder.UseStartup<TStartup>();
|
||||
|
||||
public WebApplicationFactory<TStartup> Factory { get; }
|
||||
public HttpClient Client { get; }
|
||||
|
||||
[Fact]
|
||||
|
|
@ -100,6 +101,17 @@ namespace Microsoft.AspNetCore.Mvc.FunctionalTests
|
|||
Assert.Equal("\"Hello Mr. 🦊\"", await response.Content.ReadAsStringAsync());
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public virtual async Task Formatting_StringValueWithNonAsciiCharacters()
|
||||
{
|
||||
// Act
|
||||
var response = await Client.GetAsync($"/JsonOutputFormatter/{nameof(JsonOutputFormatterController.StringWithNonAsciiContent)}");
|
||||
|
||||
// Assert
|
||||
await response.AssertStatusCodeAsync(HttpStatusCode.OK);
|
||||
Assert.Equal("\"Une bête de cirque\"", await response.Content.ReadAsStringAsync());
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public virtual async Task Formatting_SimpleModel()
|
||||
{
|
||||
|
|
|
|||
|
|
@ -2,8 +2,10 @@
|
|||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using System.Net;
|
||||
using System.Text.Encodings.Web;
|
||||
using System.Threading.Tasks;
|
||||
using FormatterWebSite.Controllers;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Xunit;
|
||||
|
||||
namespace Microsoft.AspNetCore.Mvc.FunctionalTests
|
||||
|
|
@ -15,7 +17,7 @@ namespace Microsoft.AspNetCore.Mvc.FunctionalTests
|
|||
{
|
||||
}
|
||||
|
||||
[Fact(Skip = "https://github.com/aspnet/AspNetCore/issues/11459")]
|
||||
[Fact]
|
||||
public override Task SerializableErrorIsReturnedInExpectedFormat() => base.SerializableErrorIsReturnedInExpectedFormat();
|
||||
|
||||
[Fact]
|
||||
|
|
@ -29,6 +31,25 @@ namespace Microsoft.AspNetCore.Mvc.FunctionalTests
|
|||
Assert.Equal("\"Hello Mr. \\uD83E\\uDD8A\"", await response.Content.ReadAsStringAsync());
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task Formatting_WithCustomEncoder()
|
||||
{
|
||||
// Arrange
|
||||
static void ConfigureServices(IServiceCollection serviceCollection)
|
||||
{
|
||||
serviceCollection.AddControllers()
|
||||
.AddJsonOptions(o => o.JsonSerializerOptions.Encoder = JavaScriptEncoder.Default);
|
||||
}
|
||||
var client = Factory.WithWebHostBuilder(c => c.ConfigureServices(ConfigureServices)).CreateClient();
|
||||
|
||||
// Act
|
||||
var response = await client.GetAsync($"/JsonOutputFormatter/{nameof(JsonOutputFormatterController.StringWithNonAsciiContent)}");
|
||||
|
||||
// Assert
|
||||
await response.AssertStatusCodeAsync(HttpStatusCode.OK);
|
||||
Assert.Equal("\"Une b\\u00EAte de cirque\"", await response.Content.ReadAsStringAsync());
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public override Task Formatting_DictionaryType() => base.Formatting_DictionaryType();
|
||||
|
||||
|
|
|
|||
|
|
@ -20,6 +20,9 @@ namespace FormatterWebSite.Controllers
|
|||
[HttpGet]
|
||||
public ActionResult<string> StringWithUnicodeResult() => "Hello Mr. 🦊";
|
||||
|
||||
[HttpGet]
|
||||
public ActionResult<string> StringWithNonAsciiContent() => "Une bête de cirque";
|
||||
|
||||
[HttpGet]
|
||||
public ActionResult<SimpleModel> SimpleModelResult() =>
|
||||
new SimpleModel { Id = 10, Name = "Test", StreetName = "Some street" };
|
||||
|
|
|
|||
Loading…
Reference in New Issue