Cleanup MvcJsonHelper (#6529)
* Cleanup MvcJsonHelper * Remove dependency on JsonOutputFormatter * Cache JsonSerializer for the default case
This commit is contained in:
parent
26c78ee542
commit
55ec35bb80
|
|
@ -23,6 +23,10 @@ namespace Microsoft.AspNetCore.Mvc.Rendering
|
||||||
/// The <see cref="JsonSerializerSettings"/> to be used by the serializer.
|
/// The <see cref="JsonSerializerSettings"/> to be used by the serializer.
|
||||||
/// </param>
|
/// </param>
|
||||||
/// <returns>A new <see cref="IHtmlContent"/> containing the serialized JSON.</returns>
|
/// <returns>A new <see cref="IHtmlContent"/> containing the serialized JSON.</returns>
|
||||||
|
/// <remarks>
|
||||||
|
/// The value for <see cref="JsonSerializerSettings.StringEscapeHandling" /> from <paramref name="serializerSettings"/>
|
||||||
|
/// is ignored by this method and <see cref="StringEscapeHandling.EscapeHtml"/> is always used.
|
||||||
|
/// </remarks>
|
||||||
public static IHtmlContent Serialize(
|
public static IHtmlContent Serialize(
|
||||||
this IJsonHelper jsonHelper,
|
this IJsonHelper jsonHelper,
|
||||||
object value,
|
object value,
|
||||||
|
|
|
||||||
|
|
@ -6,8 +6,8 @@ using System.Buffers;
|
||||||
using System.Globalization;
|
using System.Globalization;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using Microsoft.AspNetCore.Html;
|
using Microsoft.AspNetCore.Html;
|
||||||
using Microsoft.AspNetCore.Mvc.Formatters;
|
|
||||||
using Microsoft.AspNetCore.Mvc.Rendering;
|
using Microsoft.AspNetCore.Mvc.Rendering;
|
||||||
|
using Microsoft.Extensions.Options;
|
||||||
using Newtonsoft.Json;
|
using Newtonsoft.Json;
|
||||||
|
|
||||||
namespace Microsoft.AspNetCore.Mvc.NewtonsoftJson
|
namespace Microsoft.AspNetCore.Mvc.NewtonsoftJson
|
||||||
|
|
@ -17,42 +17,39 @@ namespace Microsoft.AspNetCore.Mvc.NewtonsoftJson
|
||||||
/// </summary>
|
/// </summary>
|
||||||
internal class NewtonsoftJsonHelper : IJsonHelper
|
internal class NewtonsoftJsonHelper : IJsonHelper
|
||||||
{
|
{
|
||||||
private readonly NewtonsoftJsonOutputFormatter _jsonOutputFormatter;
|
// Perf: JsonSerializers are relatively expensive to create, and are thread safe. Cache the serializer
|
||||||
private readonly ArrayPool<char> _charPool;
|
private readonly JsonSerializer _defaultSettingsJsonSerializer;
|
||||||
|
private readonly IArrayPool<char> _charPool;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Initializes a new instance of <see cref="NewtonsoftJsonHelper"/> that is backed by <paramref name="jsonOutputFormatter"/>.
|
/// Initializes a new instance of <see cref="NewtonsoftJsonHelper"/>.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="jsonOutputFormatter">The <see cref="NewtonsoftJsonOutputFormatter"/> used to serialize JSON.</param>
|
/// <param name="options">The <see cref="MvcNewtonsoftJsonOptions"/>.</param>
|
||||||
/// <param name="charPool">
|
/// <param name="charPool">
|
||||||
/// The <see cref="ArrayPool{Char}"/> for use with custom <see cref="JsonSerializerSettings"/> (see
|
/// The <see cref="ArrayPool{Char}"/> for use with custom <see cref="JsonSerializerSettings"/> (see
|
||||||
/// <see cref="Serialize(object, JsonSerializerSettings)"/>).
|
/// <see cref="Serialize(object, JsonSerializerSettings)"/>).
|
||||||
/// </param>
|
/// </param>
|
||||||
public NewtonsoftJsonHelper(NewtonsoftJsonOutputFormatter jsonOutputFormatter, ArrayPool<char> charPool)
|
public NewtonsoftJsonHelper(IOptions<MvcNewtonsoftJsonOptions> options, ArrayPool<char> charPool)
|
||||||
{
|
{
|
||||||
if (jsonOutputFormatter == null)
|
if (options == null)
|
||||||
{
|
{
|
||||||
throw new ArgumentNullException(nameof(jsonOutputFormatter));
|
throw new ArgumentNullException(nameof(options));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (charPool == null)
|
if (charPool == null)
|
||||||
{
|
{
|
||||||
throw new ArgumentNullException(nameof(charPool));
|
throw new ArgumentNullException(nameof(charPool));
|
||||||
}
|
}
|
||||||
|
|
||||||
_jsonOutputFormatter = jsonOutputFormatter;
|
_defaultSettingsJsonSerializer = CreateHtmlSafeSerializer(options.Value.SerializerSettings);
|
||||||
_charPool = charPool;
|
_charPool = new JsonArrayPool<char>(charPool);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc />
|
|
||||||
public IHtmlContent Serialize(object value)
|
public IHtmlContent Serialize(object value)
|
||||||
{
|
{
|
||||||
var settings = ShallowCopy(_jsonOutputFormatter.PublicSerializerSettings);
|
return Serialize(value, _defaultSettingsJsonSerializer);
|
||||||
settings.StringEscapeHandling = StringEscapeHandling.EscapeHtml;
|
|
||||||
|
|
||||||
return Serialize(value, settings);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc />
|
|
||||||
public IHtmlContent Serialize(object value, JsonSerializerSettings serializerSettings)
|
public IHtmlContent Serialize(object value, JsonSerializerSettings serializerSettings)
|
||||||
{
|
{
|
||||||
if (serializerSettings == null)
|
if (serializerSettings == null)
|
||||||
|
|
@ -60,54 +57,34 @@ namespace Microsoft.AspNetCore.Mvc.NewtonsoftJson
|
||||||
throw new ArgumentNullException(nameof(serializerSettings));
|
throw new ArgumentNullException(nameof(serializerSettings));
|
||||||
}
|
}
|
||||||
|
|
||||||
var jsonOutputFormatter = new NewtonsoftJsonOutputFormatter(serializerSettings, _charPool);
|
var jsonSerializer = CreateHtmlSafeSerializer(serializerSettings);
|
||||||
|
return Serialize(value, jsonSerializer);
|
||||||
return SerializeInternal(jsonOutputFormatter, value);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private IHtmlContent SerializeInternal(NewtonsoftJsonOutputFormatter jsonOutputFormatter, object value)
|
private IHtmlContent Serialize(object value, JsonSerializer jsonSerializer)
|
||||||
{
|
{
|
||||||
var stringWriter = new StringWriter(CultureInfo.InvariantCulture);
|
using (var stringWriter = new StringWriter(CultureInfo.InvariantCulture))
|
||||||
jsonOutputFormatter.WriteObject(stringWriter, value);
|
|
||||||
|
|
||||||
return new HtmlString(stringWriter.ToString());
|
|
||||||
}
|
|
||||||
|
|
||||||
private static JsonSerializerSettings ShallowCopy(JsonSerializerSettings settings)
|
|
||||||
{
|
|
||||||
var copiedSettings = new JsonSerializerSettings
|
|
||||||
{
|
{
|
||||||
FloatParseHandling = settings.FloatParseHandling,
|
var jsonWriter = new JsonTextWriter(stringWriter)
|
||||||
FloatFormatHandling = settings.FloatFormatHandling,
|
{
|
||||||
DateParseHandling = settings.DateParseHandling,
|
ArrayPool = _charPool,
|
||||||
DateTimeZoneHandling = settings.DateTimeZoneHandling,
|
};
|
||||||
DateFormatHandling = settings.DateFormatHandling,
|
|
||||||
Formatting = settings.Formatting,
|
|
||||||
MaxDepth = settings.MaxDepth,
|
|
||||||
DateFormatString = settings.DateFormatString,
|
|
||||||
Context = settings.Context,
|
|
||||||
Error = settings.Error,
|
|
||||||
SerializationBinder = settings.SerializationBinder,
|
|
||||||
TraceWriter = settings.TraceWriter,
|
|
||||||
Culture = settings.Culture,
|
|
||||||
ReferenceResolverProvider = settings.ReferenceResolverProvider,
|
|
||||||
EqualityComparer = settings.EqualityComparer,
|
|
||||||
ContractResolver = settings.ContractResolver,
|
|
||||||
ConstructorHandling = settings.ConstructorHandling,
|
|
||||||
TypeNameAssemblyFormatHandling = settings.TypeNameAssemblyFormatHandling,
|
|
||||||
MetadataPropertyHandling = settings.MetadataPropertyHandling,
|
|
||||||
TypeNameHandling = settings.TypeNameHandling,
|
|
||||||
PreserveReferencesHandling = settings.PreserveReferencesHandling,
|
|
||||||
Converters = settings.Converters,
|
|
||||||
DefaultValueHandling = settings.DefaultValueHandling,
|
|
||||||
NullValueHandling = settings.NullValueHandling,
|
|
||||||
ObjectCreationHandling = settings.ObjectCreationHandling,
|
|
||||||
MissingMemberHandling = settings.MissingMemberHandling,
|
|
||||||
ReferenceLoopHandling = settings.ReferenceLoopHandling,
|
|
||||||
CheckAdditionalContent = settings.CheckAdditionalContent,
|
|
||||||
};
|
|
||||||
|
|
||||||
return copiedSettings;
|
using (jsonWriter)
|
||||||
|
{
|
||||||
|
jsonSerializer.Serialize(jsonWriter, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
return new HtmlString(stringWriter.ToString());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static JsonSerializer CreateHtmlSafeSerializer(JsonSerializerSettings serializerSettings)
|
||||||
|
{
|
||||||
|
var jsonSerializer = JsonSerializer.Create(serializerSettings);
|
||||||
|
// Ignore the user configured StringEscapeHandling and always escape it.
|
||||||
|
jsonSerializer.StringEscapeHandling = StringEscapeHandling.EscapeHtml;
|
||||||
|
return jsonSerializer;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,6 @@
|
||||||
|
|
||||||
using System;
|
using System;
|
||||||
using System.Buffers;
|
using System.Buffers;
|
||||||
using System.ComponentModel;
|
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
|
@ -63,36 +62,6 @@ namespace Microsoft.AspNetCore.Mvc.Formatters
|
||||||
/// </remarks>
|
/// </remarks>
|
||||||
protected JsonSerializerSettings SerializerSettings { get; }
|
protected JsonSerializerSettings SerializerSettings { get; }
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets the <see cref="JsonSerializerSettings"/> used to configure the <see cref="JsonSerializer"/>.
|
|
||||||
/// </summary>
|
|
||||||
/// <remarks>
|
|
||||||
/// Any modifications to the <see cref="JsonSerializerSettings"/> object after this
|
|
||||||
/// <see cref="NewtonsoftJsonOutputFormatter"/> has been used will have no effect.
|
|
||||||
/// </remarks>
|
|
||||||
[EditorBrowsable(EditorBrowsableState.Never)]
|
|
||||||
public JsonSerializerSettings PublicSerializerSettings => SerializerSettings;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Writes the given <paramref name="value"/> as JSON using the given
|
|
||||||
/// <paramref name="writer"/>.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="writer">The <see cref="TextWriter"/> used to write the <paramref name="value"/></param>
|
|
||||||
/// <param name="value">The value to write as JSON.</param>
|
|
||||||
public void WriteObject(TextWriter writer, object value)
|
|
||||||
{
|
|
||||||
if (writer == null)
|
|
||||||
{
|
|
||||||
throw new ArgumentNullException(nameof(writer));
|
|
||||||
}
|
|
||||||
|
|
||||||
using (var jsonWriter = CreateJsonWriter(writer))
|
|
||||||
{
|
|
||||||
var jsonSerializer = CreateJsonSerializer();
|
|
||||||
jsonSerializer.Serialize(jsonWriter, value);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Called during serialization to create the <see cref="JsonWriter"/>.
|
/// Called during serialization to create the <see cref="JsonWriter"/>.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|
@ -145,7 +114,11 @@ namespace Microsoft.AspNetCore.Mvc.Formatters
|
||||||
var response = context.HttpContext.Response;
|
var response = context.HttpContext.Response;
|
||||||
using (var writer = context.WriterFactory(response.Body, selectedEncoding))
|
using (var writer = context.WriterFactory(response.Body, selectedEncoding))
|
||||||
{
|
{
|
||||||
WriteObject(writer, context.Object);
|
using (var jsonWriter = CreateJsonWriter(writer))
|
||||||
|
{
|
||||||
|
var jsonSerializer = CreateJsonSerializer();
|
||||||
|
jsonSerializer.Serialize(jsonWriter, context.Object);
|
||||||
|
}
|
||||||
|
|
||||||
// Perf: call FlushAsync to call WriteAsync on the stream with any content left in the TextWriter's
|
// Perf: call FlushAsync to call WriteAsync on the stream with any content left in the TextWriter's
|
||||||
// buffers. This is better than just letting dispose handle it (which would result in a synchronous
|
// buffers. This is better than just letting dispose handle it (which would result in a synchronous
|
||||||
|
|
|
||||||
|
|
@ -265,7 +265,7 @@ namespace Microsoft.AspNetCore.Mvc.FunctionalTests
|
||||||
public async Task JsonHelperWithSettings_RendersJson_WithNamesUnchanged()
|
public async Task JsonHelperWithSettings_RendersJson_WithNamesUnchanged()
|
||||||
{
|
{
|
||||||
// Arrange
|
// Arrange
|
||||||
var json = "{\"id\":9000,\"FullName\":\"John <b>Smith</b>\"}";
|
var json = "{\"id\":9000,\"FullName\":\"John \\u003cb\\u003eSmith\\u003c/b\\u003e\"}";
|
||||||
var expectedBody = string.Format(
|
var expectedBody = string.Format(
|
||||||
@"<script type=""text/javascript"">
|
@"<script type=""text/javascript"">
|
||||||
var json = {0};
|
var json = {0};
|
||||||
|
|
@ -287,7 +287,7 @@ namespace Microsoft.AspNetCore.Mvc.FunctionalTests
|
||||||
public async Task JsonHelperWithSettings_RendersJson_WithSnakeCaseNames()
|
public async Task JsonHelperWithSettings_RendersJson_WithSnakeCaseNames()
|
||||||
{
|
{
|
||||||
// Arrange
|
// Arrange
|
||||||
var json = "{\"id\":9000,\"full_name\":\"John <b>Smith</b>\"}";
|
var json = "{\"id\":9000,\"full_name\":\"John \\u003cb\\u003eSmith\\u003c/b\\u003e\"}";
|
||||||
var expectedBody = string.Format(
|
var expectedBody = string.Format(
|
||||||
@"<script type=""text/javascript"">
|
@"<script type=""text/javascript"">
|
||||||
var json = {0};
|
var json = {0};
|
||||||
|
|
|
||||||
|
|
@ -1,68 +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.Buffers;
|
|
||||||
using Microsoft.AspNetCore.Html;
|
|
||||||
using Microsoft.AspNetCore.Mvc.Formatters;
|
|
||||||
using Newtonsoft.Json;
|
|
||||||
using Newtonsoft.Json.Serialization;
|
|
||||||
using Xunit;
|
|
||||||
|
|
||||||
namespace Microsoft.AspNetCore.Mvc.NewtonsoftJson
|
|
||||||
{
|
|
||||||
public class JsonHelperTest
|
|
||||||
{
|
|
||||||
[Fact]
|
|
||||||
public void Serialize_EscapesHtmlByDefault()
|
|
||||||
{
|
|
||||||
// Arrange
|
|
||||||
var settings = new JsonSerializerSettings()
|
|
||||||
{
|
|
||||||
StringEscapeHandling = StringEscapeHandling.EscapeNonAscii,
|
|
||||||
};
|
|
||||||
var helper = new NewtonsoftJsonHelper(
|
|
||||||
new NewtonsoftJsonOutputFormatter(settings, ArrayPool<char>.Shared),
|
|
||||||
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]
|
|
||||||
public void Serialize_MaintainsSettingsAndEscapesHtml()
|
|
||||||
{
|
|
||||||
// Arrange
|
|
||||||
var settings = new JsonSerializerSettings()
|
|
||||||
{
|
|
||||||
ContractResolver = new DefaultContractResolver
|
|
||||||
{
|
|
||||||
NamingStrategy = new CamelCaseNamingStrategy(),
|
|
||||||
},
|
|
||||||
};
|
|
||||||
var helper = new NewtonsoftJsonHelper(
|
|
||||||
new NewtonsoftJsonOutputFormatter(settings, ArrayPool<char>.Shared),
|
|
||||||
ArrayPool<char>.Shared);
|
|
||||||
var obj = new
|
|
||||||
{
|
|
||||||
FullHtml = "<b>John Doe</b>"
|
|
||||||
};
|
|
||||||
var expectedOutput = "{\"fullHtml\":\"\\u003cb\\u003eJohn Doe\\u003c/b\\u003e\"}";
|
|
||||||
|
|
||||||
// Act
|
|
||||||
var result = helper.Serialize(obj);
|
|
||||||
|
|
||||||
// Assert
|
|
||||||
var htmlString = Assert.IsType<HtmlString>(result);
|
|
||||||
Assert.Equal(expectedOutput, htmlString.ToString());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -0,0 +1,95 @@
|
||||||
|
// 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.Buffers;
|
||||||
|
using Microsoft.AspNetCore.Html;
|
||||||
|
using Microsoft.Extensions.Options;
|
||||||
|
using Newtonsoft.Json;
|
||||||
|
using Newtonsoft.Json.Serialization;
|
||||||
|
using Xunit;
|
||||||
|
|
||||||
|
namespace Microsoft.AspNetCore.Mvc.NewtonsoftJson
|
||||||
|
{
|
||||||
|
public class NewtonsoftJsonHelperTest
|
||||||
|
{
|
||||||
|
[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]
|
||||||
|
public void Serialize_MaintainsSettingsAndEscapesHtml()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var options = new MvcNewtonsoftJsonOptions();
|
||||||
|
options.SerializerSettings.ContractResolver = new DefaultContractResolver
|
||||||
|
{
|
||||||
|
NamingStrategy = new DefaultNamingStrategy(),
|
||||||
|
};
|
||||||
|
var helper = new NewtonsoftJsonHelper(
|
||||||
|
Options.Create(options),
|
||||||
|
ArrayPool<char>.Shared);
|
||||||
|
var obj = new
|
||||||
|
{
|
||||||
|
FullHtml = "<b>John Doe</b>"
|
||||||
|
};
|
||||||
|
var expectedOutput = "{\"FullHtml\":\"\\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_UsesHtmlSafeVersionOfPassedInSettings()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var options = new MvcNewtonsoftJsonOptions();
|
||||||
|
var helper = new NewtonsoftJsonHelper(
|
||||||
|
Options.Create(options),
|
||||||
|
ArrayPool<char>.Shared);
|
||||||
|
var obj = new
|
||||||
|
{
|
||||||
|
FullHtml = "<b>John Doe</b>"
|
||||||
|
};
|
||||||
|
var serializerSettings = new JsonSerializerSettings
|
||||||
|
{
|
||||||
|
StringEscapeHandling = StringEscapeHandling.Default,
|
||||||
|
ContractResolver = new DefaultContractResolver
|
||||||
|
{
|
||||||
|
NamingStrategy = new SnakeCaseNamingStrategy(),
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
var expectedOutput = "{\"full_html\":\"\\u003cb\\u003eJohn Doe\\u003c/b\\u003e\"}";
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var result = helper.Serialize(obj, serializerSettings);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
var htmlString = Assert.IsType<HtmlString>(result);
|
||||||
|
Assert.Equal(expectedOutput, htmlString.ToString());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -10,7 +10,6 @@ using System.Text;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using Microsoft.AspNetCore.Http;
|
using Microsoft.AspNetCore.Http;
|
||||||
using Microsoft.AspNetCore.Mvc.Abstractions;
|
using Microsoft.AspNetCore.Mvc.Abstractions;
|
||||||
using Microsoft.AspNetCore.Mvc.Formatters;
|
|
||||||
using Microsoft.AspNetCore.Mvc.NewtonsoftJson;
|
using Microsoft.AspNetCore.Mvc.NewtonsoftJson;
|
||||||
using Microsoft.AspNetCore.Routing;
|
using Microsoft.AspNetCore.Routing;
|
||||||
using Microsoft.Extensions.Logging;
|
using Microsoft.Extensions.Logging;
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue