diff --git a/src/Microsoft.AspNet.Mvc.Formatters.Json/Internal/JsonArrayPool.cs b/src/Microsoft.AspNet.Mvc.Formatters.Json/Internal/JsonArrayPool.cs new file mode 100644 index 0000000000..b3501caeda --- /dev/null +++ b/src/Microsoft.AspNet.Mvc.Formatters.Json/Internal/JsonArrayPool.cs @@ -0,0 +1,39 @@ +// 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.Buffers; +using Newtonsoft.Json; + +namespace Microsoft.AspNet.Mvc.Formatters.Json.Internal +{ + public class JsonArrayPool : IArrayPool + { + private readonly ArrayPool _inner; + + public JsonArrayPool(ArrayPool inner) + { + if (inner == null) + { + throw new ArgumentNullException(nameof(inner)); + } + + _inner = inner; + } + + public T[] Rent(int minimumLength) + { + return _inner.Rent(minimumLength); + } + + public void Return(T[] array) + { + if (array == null) + { + throw new ArgumentNullException(nameof(array)); + } + + _inner.Return(array); + } + } +} diff --git a/src/Microsoft.AspNet.Mvc.Formatters.Json/Internal/MvcJsonMvcOptionsSetup.cs b/src/Microsoft.AspNet.Mvc.Formatters.Json/Internal/MvcJsonMvcOptionsSetup.cs index c6624b05dd..962cbafd33 100644 --- a/src/Microsoft.AspNet.Mvc.Formatters.Json/Internal/MvcJsonMvcOptionsSetup.cs +++ b/src/Microsoft.AspNet.Mvc.Formatters.Json/Internal/MvcJsonMvcOptionsSetup.cs @@ -1,6 +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.Buffers; using Microsoft.AspNet.Mvc.ModelBinding.Validation; using Microsoft.Extensions.Logging; using Microsoft.Extensions.Options; @@ -12,22 +13,26 @@ namespace Microsoft.AspNet.Mvc.Formatters.Json.Internal { public class MvcJsonMvcOptionsSetup : ConfigureOptions { - public MvcJsonMvcOptionsSetup(ILoggerFactory loggerFactory, IOptions jsonOptions) - : base((_) => ConfigureMvc(_, jsonOptions.Value.SerializerSettings, loggerFactory)) + public MvcJsonMvcOptionsSetup( + ILoggerFactory loggerFactory, + IOptions jsonOptions, + ArrayPool charPool) + : base((options) => ConfigureMvc(options, jsonOptions.Value.SerializerSettings, loggerFactory, charPool)) { } public static void ConfigureMvc( MvcOptions options, JsonSerializerSettings serializerSettings, - ILoggerFactory loggerFactory) + ILoggerFactory loggerFactory, + ArrayPool charPool) { var jsonInputLogger = loggerFactory.CreateLogger(); var jsonInputPatchLogger = loggerFactory.CreateLogger(); - options.OutputFormatters.Add(new JsonOutputFormatter(serializerSettings)); - options.InputFormatters.Add(new JsonInputFormatter(jsonInputLogger, serializerSettings)); - options.InputFormatters.Add(new JsonPatchInputFormatter(jsonInputPatchLogger, serializerSettings)); + options.OutputFormatters.Add(new JsonOutputFormatter(serializerSettings, charPool)); + options.InputFormatters.Add(new JsonInputFormatter(jsonInputLogger, serializerSettings, charPool)); + options.InputFormatters.Add(new JsonPatchInputFormatter(jsonInputPatchLogger, serializerSettings, charPool)); options.FormatterMappings.SetMediaTypeMappingForFormat("json", MediaTypeHeaderValue.Parse("application/json")); diff --git a/src/Microsoft.AspNet.Mvc.Formatters.Json/JsonInputFormatter.cs b/src/Microsoft.AspNet.Mvc.Formatters.Json/JsonInputFormatter.cs index 903ffbaf98..77150163bd 100644 --- a/src/Microsoft.AspNet.Mvc.Formatters.Json/JsonInputFormatter.cs +++ b/src/Microsoft.AspNet.Mvc.Formatters.Json/JsonInputFormatter.cs @@ -2,7 +2,9 @@ // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System; +using System.Buffers; using System.Threading.Tasks; +using Microsoft.AspNet.Mvc.Formatters.Json.Internal; using Microsoft.AspNet.Mvc.Formatters.Json.Logging; using Microsoft.AspNet.Mvc.Internal; using Microsoft.AspNet.Mvc.ModelBinding; @@ -13,16 +15,22 @@ namespace Microsoft.AspNet.Mvc.Formatters { public class JsonInputFormatter : InputFormatter { - private JsonSerializerSettings _serializerSettings; - private ILogger _logger; + private readonly IArrayPool _charPool; + private readonly ILogger _logger; + private JsonSerializerSettings _serializerSettings; public JsonInputFormatter(ILogger logger) - : this(logger, SerializerSettingsProvider.CreateSerializerSettings()) + : this(logger, SerializerSettingsProvider.CreateSerializerSettings(), ArrayPool.Shared) { } public JsonInputFormatter(ILogger logger, JsonSerializerSettings serializerSettings) + : this(logger, serializerSettings, ArrayPool.Shared) + { + } + + public JsonInputFormatter(ILogger logger, JsonSerializerSettings serializerSettings, ArrayPool charPool) { if (serializerSettings == null) { @@ -36,6 +44,7 @@ namespace Microsoft.AspNet.Mvc.Formatters _logger = logger; _serializerSettings = serializerSettings; + _charPool = new JsonArrayPool(charPool); SupportedEncodings.Add(UTF8EncodingWithoutBOM); SupportedEncodings.Add(UTF16EncodingLittleEndian); @@ -84,6 +93,7 @@ namespace Microsoft.AspNet.Mvc.Formatters { using (var jsonReader = new JsonTextReader(streamReader)) { + jsonReader.ArrayPool = _charPool; jsonReader.CloseInput = false; var successful = true; diff --git a/src/Microsoft.AspNet.Mvc.Formatters.Json/JsonOutputFormatter.cs b/src/Microsoft.AspNet.Mvc.Formatters.Json/JsonOutputFormatter.cs index 975ebf23b8..beb9a5426e 100644 --- a/src/Microsoft.AspNet.Mvc.Formatters.Json/JsonOutputFormatter.cs +++ b/src/Microsoft.AspNet.Mvc.Formatters.Json/JsonOutputFormatter.cs @@ -2,9 +2,11 @@ // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System; +using System.Buffers; using System.IO; using System.Text; using System.Threading.Tasks; +using Microsoft.AspNet.Mvc.Formatters.Json.Internal; using Microsoft.AspNet.Mvc.Internal; using Newtonsoft.Json; @@ -15,6 +17,8 @@ namespace Microsoft.AspNet.Mvc.Formatters /// public class JsonOutputFormatter : OutputFormatter { + private readonly IArrayPool _charPool; + private JsonSerializerSettings _serializerSettings; // Perf: JsonSerializers are relatively expensive to create, and are thread safe. We cache @@ -22,18 +26,29 @@ namespace Microsoft.AspNet.Mvc.Formatters private JsonSerializer _serializer; public JsonOutputFormatter() - : this(SerializerSettingsProvider.CreateSerializerSettings()) + : this(SerializerSettingsProvider.CreateSerializerSettings(), ArrayPool.Shared) { } public JsonOutputFormatter(JsonSerializerSettings serializerSettings) + : this(serializerSettings, ArrayPool.Shared) + { + } + + public JsonOutputFormatter(JsonSerializerSettings serializerSettings, ArrayPool charPool) { if (serializerSettings == null) { throw new ArgumentNullException(nameof(serializerSettings)); } + if (charPool == null) + { + throw new ArgumentNullException(nameof(charPool)); + } + _serializerSettings = serializerSettings; + _charPool = new JsonArrayPool(charPool); SupportedEncodings.Add(Encoding.UTF8); SupportedEncodings.Add(Encoding.Unicode); @@ -94,8 +109,11 @@ namespace Microsoft.AspNet.Mvc.Formatters throw new ArgumentNullException(nameof(writer)); } - var jsonWriter = new JsonTextWriter(writer); - jsonWriter.CloseOutput = false; + var jsonWriter = new JsonTextWriter(writer) + { + ArrayPool = _charPool, + CloseOutput = false, + }; return jsonWriter; } diff --git a/src/Microsoft.AspNet.Mvc.Formatters.Json/JsonPatchInputFormatter.cs b/src/Microsoft.AspNet.Mvc.Formatters.Json/JsonPatchInputFormatter.cs index 61df53c7cd..804ade8349 100644 --- a/src/Microsoft.AspNet.Mvc.Formatters.Json/JsonPatchInputFormatter.cs +++ b/src/Microsoft.AspNet.Mvc.Formatters.Json/JsonPatchInputFormatter.cs @@ -2,6 +2,7 @@ // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System; +using System.Buffers; using System.Reflection; using System.Threading.Tasks; using Microsoft.AspNet.JsonPatch; @@ -14,12 +15,15 @@ namespace Microsoft.AspNet.Mvc.Formatters public class JsonPatchInputFormatter : JsonInputFormatter { public JsonPatchInputFormatter(ILogger logger) - : this(logger, SerializerSettingsProvider.CreateSerializerSettings()) + : this(logger, SerializerSettingsProvider.CreateSerializerSettings(), ArrayPool.Shared) { } - public JsonPatchInputFormatter(ILogger logger, JsonSerializerSettings serializerSettings) - : base(logger, serializerSettings) + public JsonPatchInputFormatter( + ILogger logger, + JsonSerializerSettings serializerSettings, + ArrayPool charPool) + : base(logger, serializerSettings, charPool) { // Clear all values and only include json-patch+json value. SupportedMediaTypes.Clear(); diff --git a/src/Microsoft.AspNet.Mvc.Formatters.Json/project.json b/src/Microsoft.AspNet.Mvc.Formatters.Json/project.json index 69dcc9f46f..ff8f262fbd 100644 --- a/src/Microsoft.AspNet.Mvc.Formatters.Json/project.json +++ b/src/Microsoft.AspNet.Mvc.Formatters.Json/project.json @@ -16,7 +16,7 @@ "version": "1.0.0-*", "type": "build" }, - "Newtonsoft.Json": "6.0.6" + "Newtonsoft.Json": "8.0.1" }, "frameworks": { "net451": {}, diff --git a/src/Microsoft.AspNet.Mvc.ViewFeatures/DependencyInjection/MvcViewFeaturesMvcCoreBuilderExtensions.cs b/src/Microsoft.AspNet.Mvc.ViewFeatures/DependencyInjection/MvcViewFeaturesMvcCoreBuilderExtensions.cs index 1c70c43bcb..4dbebfa8e9 100644 --- a/src/Microsoft.AspNet.Mvc.ViewFeatures/DependencyInjection/MvcViewFeaturesMvcCoreBuilderExtensions.cs +++ b/src/Microsoft.AspNet.Mvc.ViewFeatures/DependencyInjection/MvcViewFeaturesMvcCoreBuilderExtensions.cs @@ -112,7 +112,8 @@ namespace Microsoft.Extensions.DependencyInjection services.TryAdd(ServiceDescriptor.Singleton(serviceProvider => { var options = serviceProvider.GetRequiredService>().Value; - return new JsonOutputFormatter(options.SerializerSettings); + var charPool = serviceProvider.GetRequiredService>(); + return new JsonOutputFormatter(options.SerializerSettings, charPool); })); // diff --git a/test/Microsoft.AspNet.Mvc.IntegrationTests/TestMvcOptions.cs b/test/Microsoft.AspNet.Mvc.IntegrationTests/TestMvcOptions.cs index 2696e125aa..76eaea04b7 100644 --- a/test/Microsoft.AspNet.Mvc.IntegrationTests/TestMvcOptions.cs +++ b/test/Microsoft.AspNet.Mvc.IntegrationTests/TestMvcOptions.cs @@ -1,6 +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.Buffers; using Microsoft.AspNet.Mvc.DataAnnotations; using Microsoft.AspNet.Mvc.DataAnnotations.Internal; using Microsoft.AspNet.Mvc.Formatters.Json.Internal; @@ -30,7 +31,7 @@ namespace Microsoft.AspNet.Mvc.IntegrationTests var loggerFactory = new LoggerFactory(); var serializerSettings = SerializerSettingsProvider.CreateSerializerSettings(); - MvcJsonMvcOptionsSetup.ConfigureMvc(Value, serializerSettings, loggerFactory); + MvcJsonMvcOptionsSetup.ConfigureMvc(Value, serializerSettings, loggerFactory, ArrayPool.Shared); } public MvcOptions Value { get; }