diff --git a/framework/Volo.Abp.sln b/framework/Volo.Abp.sln index 82f67fe15c..b646c5f9e6 100644 --- a/framework/Volo.Abp.sln +++ b/framework/Volo.Abp.sln @@ -415,6 +415,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Volo.Abp.Json.SystemTextJso EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Volo.Abp.Json.Core", "src\Volo.Abp.Json.Core\Volo.Abp.Json.Core.csproj", "{08531C5D-0436-4721-986D-96446CF54316}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Volo.Abp.AspNetCore.Mvc.NewtonsoftJson", "src\Volo.Abp.AspNetCore.Mvc.NewtonsoftJson\Volo.Abp.AspNetCore.Mvc.NewtonsoftJson.csproj", "{0CFC9D4F-F12F-4B44-ABCF-AB4A0E9E85B2}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -1237,6 +1239,10 @@ Global {08531C5D-0436-4721-986D-96446CF54316}.Debug|Any CPU.Build.0 = Debug|Any CPU {08531C5D-0436-4721-986D-96446CF54316}.Release|Any CPU.ActiveCfg = Release|Any CPU {08531C5D-0436-4721-986D-96446CF54316}.Release|Any CPU.Build.0 = Release|Any CPU + {0CFC9D4F-F12F-4B44-ABCF-AB4A0E9E85B2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {0CFC9D4F-F12F-4B44-ABCF-AB4A0E9E85B2}.Debug|Any CPU.Build.0 = Debug|Any CPU + {0CFC9D4F-F12F-4B44-ABCF-AB4A0E9E85B2}.Release|Any CPU.ActiveCfg = Release|Any CPU + {0CFC9D4F-F12F-4B44-ABCF-AB4A0E9E85B2}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -1446,6 +1452,7 @@ Global {9DD41C8F-0886-483C-B98B-C55EAA7F226D} = {5DF0E140-0513-4D0D-BE2E-3D4D85CD70E6} {0AD06E14-CBFE-4551-8D18-9E921D8F2A87} = {5DF0E140-0513-4D0D-BE2E-3D4D85CD70E6} {08531C5D-0436-4721-986D-96446CF54316} = {5DF0E140-0513-4D0D-BE2E-3D4D85CD70E6} + {0CFC9D4F-F12F-4B44-ABCF-AB4A0E9E85B2} = {5DF0E140-0513-4D0D-BE2E-3D4D85CD70E6} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {BB97ECF4-9A84-433F-A80B-2A3285BDD1D5} diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc.NewtonsoftJson/FodyWeavers.xml b/framework/src/Volo.Abp.AspNetCore.Mvc.NewtonsoftJson/FodyWeavers.xml new file mode 100644 index 0000000000..bc5a74a236 --- /dev/null +++ b/framework/src/Volo.Abp.AspNetCore.Mvc.NewtonsoftJson/FodyWeavers.xml @@ -0,0 +1,3 @@ + + + diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc.NewtonsoftJson/FodyWeavers.xsd b/framework/src/Volo.Abp.AspNetCore.Mvc.NewtonsoftJson/FodyWeavers.xsd new file mode 100644 index 0000000000..3f3946e282 --- /dev/null +++ b/framework/src/Volo.Abp.AspNetCore.Mvc.NewtonsoftJson/FodyWeavers.xsd @@ -0,0 +1,30 @@ + + + + + + + + + + + + + + + 'true' to run assembly verification (PEVerify) on the target assembly after all weavers have been executed. + + + + + A comma-separated list of error codes that can be safely ignored in assembly verification. + + + + + 'false' to turn off automatic generation of the XML Schema file. + + + + + \ No newline at end of file diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc.NewtonsoftJson/Volo.Abp.AspNetCore.Mvc.NewtonsoftJson.csproj b/framework/src/Volo.Abp.AspNetCore.Mvc.NewtonsoftJson/Volo.Abp.AspNetCore.Mvc.NewtonsoftJson.csproj new file mode 100644 index 0000000000..9e8b8951e3 --- /dev/null +++ b/framework/src/Volo.Abp.AspNetCore.Mvc.NewtonsoftJson/Volo.Abp.AspNetCore.Mvc.NewtonsoftJson.csproj @@ -0,0 +1,28 @@ + + + + + + + net6.0 + true + Volo.Abp.AspNetCore.Mvc.NewtonsoftJson + Volo.Abp.AspNetCore.Mvc.NewtonsoftJson + $(AssetTargetFallback);portable-net45+win8+wp8+wpa81; + false + false + false + true + Library + + + + + + + + + + + + diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc.NewtonsoftJson/Volo/Abp/AspNetCore/Mvc/NewtonsoftJson/AbpAspNetCoreMvcNewtonsoftModule.cs b/framework/src/Volo.Abp.AspNetCore.Mvc.NewtonsoftJson/Volo/Abp/AspNetCore/Mvc/NewtonsoftJson/AbpAspNetCoreMvcNewtonsoftModule.cs new file mode 100644 index 0000000000..ccf25979db --- /dev/null +++ b/framework/src/Volo.Abp.AspNetCore.Mvc.NewtonsoftJson/Volo/Abp/AspNetCore/Mvc/NewtonsoftJson/AbpAspNetCoreMvcNewtonsoftModule.cs @@ -0,0 +1,42 @@ +using Microsoft.AspNetCore.Mvc; +using Microsoft.AspNetCore.Mvc.Formatters; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.DependencyInjection.Extensions; +using Microsoft.Extensions.ObjectPool; +using Microsoft.Extensions.Options; +using Volo.Abp.AspNetCore.Mvc.Json; +using Volo.Abp.Modularity; + +namespace Volo.Abp.AspNetCore.Mvc.NewtonsoftJson; + +[DependsOn(typeof(AbpAspNetCoreMvcModule))] +public class AbpAspNetCoreMvcNewtonsoftModule : AbpModule +{ + public override void ConfigureServices(ServiceConfigurationContext context) + { + var options = context.Services.ExecutePreConfiguredActions(); + + if (options.UseHybridSerializer) + { + context.Services.TryAddSingleton(); + + Configure(mvcOptions => + { + mvcOptions.InputFormatters.RemoveType(); + mvcOptions.OutputFormatters.RemoveType(); + }); + + Configure(formatterOptions => + { + formatterOptions.TextInputFormatters.Add(); + formatterOptions.TextOutputFormatters.Add(); + }); + } + else + { + context.Services.AddMvcCore().AddNewtonsoftJson(); + } + + context.Services.TryAddEnumerable(ServiceDescriptor.Transient, AbpMvcNewtonsoftJsonOptionsSetup>()); + } +} diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc.NewtonsoftJson/Volo/Abp/AspNetCore/Mvc/NewtonsoftJson/AbpAspNetCoreMvcNewtonsoftOptions.cs b/framework/src/Volo.Abp.AspNetCore.Mvc.NewtonsoftJson/Volo/Abp/AspNetCore/Mvc/NewtonsoftJson/AbpAspNetCoreMvcNewtonsoftOptions.cs new file mode 100644 index 0000000000..63d11b00b7 --- /dev/null +++ b/framework/src/Volo.Abp.AspNetCore.Mvc.NewtonsoftJson/Volo/Abp/AspNetCore/Mvc/NewtonsoftJson/AbpAspNetCoreMvcNewtonsoftOptions.cs @@ -0,0 +1,11 @@ +namespace Volo.Abp.AspNetCore.Mvc.NewtonsoftJson; + +public class AbpAspNetCoreMvcNewtonsoftOptions +{ + public bool UseHybridSerializer { get; set; } + + public AbpAspNetCoreMvcNewtonsoftOptions() + { + UseHybridSerializer = true; + } +} diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/Json/AbpMvcNewtonsoftJsonOptionsSetup.cs b/framework/src/Volo.Abp.AspNetCore.Mvc.NewtonsoftJson/Volo/Abp/AspNetCore/Mvc/NewtonsoftJson/AbpMvcNewtonsoftJsonOptionsSetup.cs similarity index 83% rename from framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/Json/AbpMvcNewtonsoftJsonOptionsSetup.cs rename to framework/src/Volo.Abp.AspNetCore.Mvc.NewtonsoftJson/Volo/Abp/AspNetCore/Mvc/NewtonsoftJson/AbpMvcNewtonsoftJsonOptionsSetup.cs index c68b912372..1d1dc6a373 100644 --- a/framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/Json/AbpMvcNewtonsoftJsonOptionsSetup.cs +++ b/framework/src/Volo.Abp.AspNetCore.Mvc.NewtonsoftJson/Volo/Abp/AspNetCore/Mvc/NewtonsoftJson/AbpMvcNewtonsoftJsonOptionsSetup.cs @@ -3,7 +3,7 @@ using Microsoft.AspNetCore.Mvc; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Options; -namespace Volo.Abp.AspNetCore.Mvc.Json; +namespace Volo.Abp.AspNetCore.Mvc.NewtonsoftJson; public class AbpMvcNewtonsoftJsonOptionsSetup : IConfigureOptions { @@ -16,6 +16,6 @@ public class AbpMvcNewtonsoftJsonOptionsSetup : IConfigureOptions(); + options.SerializerSettings.ContractResolver = ServiceProvider.GetRequiredService(); } } diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/Json/AbpMvcJsonContractResolver.cs b/framework/src/Volo.Abp.AspNetCore.Mvc.NewtonsoftJson/Volo/Abp/AspNetCore/Mvc/NewtonsoftJson/AbpNewtonsoftJsonContractResolver.cs similarity index 85% rename from framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/Json/AbpMvcJsonContractResolver.cs rename to framework/src/Volo.Abp.AspNetCore.Mvc.NewtonsoftJson/Volo/Abp/AspNetCore/Mvc/NewtonsoftJson/AbpNewtonsoftJsonContractResolver.cs index a6f242d59c..f33c9abbd4 100644 --- a/framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/Json/AbpMvcJsonContractResolver.cs +++ b/framework/src/Volo.Abp.AspNetCore.Mvc.NewtonsoftJson/Volo/Abp/AspNetCore/Mvc/NewtonsoftJson/AbpNewtonsoftJsonContractResolver.cs @@ -8,13 +8,13 @@ using Volo.Abp.Json.Newtonsoft; using Volo.Abp.Reflection; using Volo.Abp.Timing; -namespace Volo.Abp.AspNetCore.Mvc.Json; +namespace Volo.Abp.AspNetCore.Mvc.NewtonsoftJson; -public class AbpMvcJsonContractResolver : DefaultContractResolver, ITransientDependency +public class AbpNewtonsoftJsonContractResolver : DefaultContractResolver, ITransientDependency { private readonly Lazy _dateTimeConverter; - public AbpMvcJsonContractResolver(IServiceProvider serviceProvider) + public AbpNewtonsoftJsonContractResolver(IServiceProvider serviceProvider) { _dateTimeConverter = new Lazy( serviceProvider.GetRequiredService, diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc.NewtonsoftJson/Volo/Abp/AspNetCore/Mvc/NewtonsoftJson/AbpNewtonsoftJsonFormatter.cs b/framework/src/Volo.Abp.AspNetCore.Mvc.NewtonsoftJson/Volo/Abp/AspNetCore/Mvc/NewtonsoftJson/AbpNewtonsoftJsonFormatter.cs new file mode 100644 index 0000000000..aa411bfb61 --- /dev/null +++ b/framework/src/Volo.Abp.AspNetCore.Mvc.NewtonsoftJson/Volo/Abp/AspNetCore/Mvc/NewtonsoftJson/AbpNewtonsoftJsonFormatter.cs @@ -0,0 +1,65 @@ +using System; +using System.Buffers; +using System.Threading.Tasks; +using Microsoft.AspNetCore.Mvc; +using Microsoft.AspNetCore.Mvc.Formatters; +using Microsoft.Extensions.Logging; +using Microsoft.Extensions.ObjectPool; +using Microsoft.Extensions.Options; +using Volo.Abp.AspNetCore.Mvc.Json; +using Volo.Abp.DependencyInjection; + +namespace Volo.Abp.AspNetCore.Mvc.NewtonsoftJson; + +public class AbpNewtonsoftJsonFormatter : IAbpHybridJsonInputFormatter, IAbpHybridJsonOutputFormatter, ITransientDependency +{ + private readonly ILoggerFactory _loggerFactory; + private readonly ArrayPool _charPool; + private readonly ObjectPoolProvider _objectPoolProvider; + private readonly IOptions _mvcOptions; + private readonly IOptions _mvcNewtonsoftJsonOptions; + + public AbpNewtonsoftJsonFormatter( + ILoggerFactory loggerFactory, + ArrayPool charPool, + ObjectPoolProvider objectPoolProvider, + IOptions mvcOptions, + IOptions mvcNewtonsoftJsonOptions) + { + _loggerFactory = loggerFactory; + _charPool = charPool; + _objectPoolProvider = objectPoolProvider; + _mvcOptions = mvcOptions; + _mvcNewtonsoftJsonOptions = mvcNewtonsoftJsonOptions; + } + + Task IAbpHybridJsonInputFormatter.CanHandleAsync(Type type) + { + return Task.FromResult(true); + } + + public Task GetTextInputFormatterAsync() + { + return Task.FromResult(new NewtonsoftJsonInputFormatter( + _loggerFactory.CreateLogger(), + _mvcNewtonsoftJsonOptions.Value.SerializerSettings, + _charPool, + _objectPoolProvider, + _mvcOptions.Value, + _mvcNewtonsoftJsonOptions.Value)); + } + + Task IAbpHybridJsonOutputFormatter.CanHandleAsync(Type type) + { + return Task.FromResult(true); + } + + public Task GetTextOutputFormatterAsync() + { + return Task.FromResult(new NewtonsoftJsonOutputFormatter( + _mvcNewtonsoftJsonOptions.Value.SerializerSettings, + _charPool, + _mvcOptions.Value, + _mvcNewtonsoftJsonOptions.Value)); + } +} diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc/Volo.Abp.AspNetCore.Mvc.csproj b/framework/src/Volo.Abp.AspNetCore.Mvc/Volo.Abp.AspNetCore.Mvc.csproj index 9113c4529e..048c2308e7 100644 --- a/framework/src/Volo.Abp.AspNetCore.Mvc/Volo.Abp.AspNetCore.Mvc.csproj +++ b/framework/src/Volo.Abp.AspNetCore.Mvc/Volo.Abp.AspNetCore.Mvc.csproj @@ -29,7 +29,6 @@ - diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/Json/AbpHybridJsonFormatterOptions.cs b/framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/Json/AbpHybridJsonFormatterOptions.cs new file mode 100644 index 0000000000..066ebe4413 --- /dev/null +++ b/framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/Json/AbpHybridJsonFormatterOptions.cs @@ -0,0 +1,17 @@ +using Microsoft.AspNetCore.Mvc.Formatters; +using Volo.Abp.Collections; + +namespace Volo.Abp.AspNetCore.Mvc.Json; + +public class AbpHybridJsonFormatterOptions +{ + public ITypeList TextInputFormatters { get; } + + public ITypeList TextOutputFormatters { get; } + + public AbpHybridJsonFormatterOptions() + { + TextInputFormatters = new TypeList(); + TextOutputFormatters = new TypeList(); + } +} diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/Json/AbpHybridJsonInputFormatter.cs b/framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/Json/AbpHybridJsonInputFormatter.cs index 69da26a317..874508ac1f 100644 --- a/framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/Json/AbpHybridJsonInputFormatter.cs +++ b/framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/Json/AbpHybridJsonInputFormatter.cs @@ -1,21 +1,16 @@ -using System.Text; +using System; +using System.Text; using System.Threading.Tasks; using Microsoft.AspNetCore.Mvc.Formatters; using Microsoft.Extensions.DependencyInjection; -using Volo.Abp.Json.SystemTextJson; +using Microsoft.Extensions.Options; namespace Volo.Abp.AspNetCore.Mvc.Json; public class AbpHybridJsonInputFormatter : TextInputFormatter, IInputFormatterExceptionPolicy { - private readonly SystemTextJsonInputFormatter _systemTextJsonInputFormatter; - private readonly NewtonsoftJsonInputFormatter _newtonsoftJsonInputFormatter; - - public AbpHybridJsonInputFormatter(SystemTextJsonInputFormatter systemTextJsonInputFormatter, NewtonsoftJsonInputFormatter newtonsoftJsonInputFormatter) + public AbpHybridJsonInputFormatter() { - _systemTextJsonInputFormatter = systemTextJsonInputFormatter; - _newtonsoftJsonInputFormatter = newtonsoftJsonInputFormatter; - SupportedEncodings.Add(UTF8EncodingWithoutBOM); SupportedEncodings.Add(UTF16EncodingLittleEndian); @@ -24,21 +19,25 @@ public class AbpHybridJsonInputFormatter : TextInputFormatter, IInputFormatterEx SupportedMediaTypes.Add(MediaTypeHeaderValues.ApplicationAnyJsonSyntax); } - public override async Task ReadRequestBodyAsync(InputFormatterContext context, Encoding encoding) + public async override Task ReadRequestBodyAsync(InputFormatterContext context, Encoding encoding) { - return await GetTextInputFormatter(context).ReadRequestBodyAsync(context, encoding); + return await (await GetTextInputFormatterAsync(context)).ReadRequestBodyAsync(context, encoding); } - protected virtual TextInputFormatter GetTextInputFormatter(InputFormatterContext context) + protected virtual async Task GetTextInputFormatterAsync(InputFormatterContext context) { - var typesMatcher = context.HttpContext.RequestServices.GetRequiredService(); + var options = context.HttpContext.RequestServices.GetRequiredService>().Value; - if (!typesMatcher.Match(context.ModelType)) + foreach (var inputFormatterType in options.TextInputFormatters) { - return _systemTextJsonInputFormatter; + var inputFormatter = context.HttpContext.RequestServices.GetRequiredService(inputFormatterType).As(); + if (await inputFormatter.CanHandleAsync(context.ModelType)) + { + return await inputFormatter.GetTextInputFormatterAsync(); + } } - return _newtonsoftJsonInputFormatter; + throw new AbpException($"The {nameof(AbpHybridJsonInputFormatter)} can't handle '{context.ModelType.GetFullNameWithAssemblyName()}'!"); } public virtual InputFormatterExceptionPolicy ExceptionPolicy => InputFormatterExceptionPolicy.MalformedInputExceptions; diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/Json/AbpHybridJsonOptionsSetup.cs b/framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/Json/AbpHybridJsonOptionsSetup.cs deleted file mode 100644 index 794bc79af9..0000000000 --- a/framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/Json/AbpHybridJsonOptionsSetup.cs +++ /dev/null @@ -1,72 +0,0 @@ -using System.Buffers; -using System.Text.Encodings.Web; -using System.Text.Json; -using Microsoft.AspNetCore.Mvc; -using Microsoft.AspNetCore.Mvc.Formatters; -using Microsoft.Extensions.Logging; -using Microsoft.Extensions.ObjectPool; -using Microsoft.Extensions.Options; - -namespace Volo.Abp.AspNetCore.Mvc.Json; - -public class AbpHybridJsonOptionsSetup : IConfigureOptions -{ - private readonly IOptions _jsonOptions; - private readonly IOptions _mvcNewtonsoftJsonOptions; - private readonly ILoggerFactory _loggerFactory; - private readonly ArrayPool _charPool; - private readonly ObjectPoolProvider _objectPoolProvider; - - public AbpHybridJsonOptionsSetup( - IOptions jsonOptions, - IOptions mvcNewtonsoftJsonOptions, - ILoggerFactory loggerFactory, - ArrayPool charPool, - ObjectPoolProvider objectPoolProvider) - { - _jsonOptions = jsonOptions; - _mvcNewtonsoftJsonOptions = mvcNewtonsoftJsonOptions; - _loggerFactory = loggerFactory; - _charPool = charPool; - _objectPoolProvider = objectPoolProvider; - } - - public void Configure(MvcOptions options) - { - var systemTextJsonInputFormatter = new SystemTextJsonInputFormatter( - _jsonOptions.Value, - _loggerFactory.CreateLogger()); - - var newtonsoftJsonInputFormatter = new NewtonsoftJsonInputFormatter( - _loggerFactory.CreateLogger(), - _mvcNewtonsoftJsonOptions.Value.SerializerSettings, - _charPool, - _objectPoolProvider, - options, - _mvcNewtonsoftJsonOptions.Value); - - options.InputFormatters.RemoveType(); - options.InputFormatters.RemoveType(); - options.InputFormatters.Add(new AbpHybridJsonInputFormatter(systemTextJsonInputFormatter, newtonsoftJsonInputFormatter)); - - var jsonSerializerOptions = _jsonOptions.Value.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 = new JsonSerializerOptions(jsonSerializerOptions) - { - Encoder = JavaScriptEncoder.UnsafeRelaxedJsonEscaping, - }; - } - - var systemTextJsonOutputFormatter = new SystemTextJsonOutputFormatter(jsonSerializerOptions); - var newtonsoftJsonOutputFormatter = new NewtonsoftJsonOutputFormatter( - _mvcNewtonsoftJsonOptions.Value.SerializerSettings, - _charPool, - options); - - options.OutputFormatters.RemoveType(); - options.OutputFormatters.RemoveType(); - options.OutputFormatters.Add(new AbpHybridJsonOutputFormatter(systemTextJsonOutputFormatter, newtonsoftJsonOutputFormatter)); - } -} diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/Json/AbpHybridJsonOutputFormatter.cs b/framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/Json/AbpHybridJsonOutputFormatter.cs index f4c5df1dc9..2960ff1635 100644 --- a/framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/Json/AbpHybridJsonOutputFormatter.cs +++ b/framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/Json/AbpHybridJsonOutputFormatter.cs @@ -1,21 +1,16 @@ -using System.Text; +using System; +using System.Text; using System.Threading.Tasks; using Microsoft.AspNetCore.Mvc.Formatters; using Microsoft.Extensions.DependencyInjection; -using Volo.Abp.Json.SystemTextJson; +using Microsoft.Extensions.Options; namespace Volo.Abp.AspNetCore.Mvc.Json; public class AbpHybridJsonOutputFormatter : TextOutputFormatter { - private readonly SystemTextJsonOutputFormatter _systemTextJsonOutputFormatter; - private readonly NewtonsoftJsonOutputFormatter _newtonsoftJsonOutputFormatter; - - public AbpHybridJsonOutputFormatter(SystemTextJsonOutputFormatter systemTextJsonOutputFormatter, NewtonsoftJsonOutputFormatter newtonsoftJsonOutputFormatter) + public AbpHybridJsonOutputFormatter() { - _systemTextJsonOutputFormatter = systemTextJsonOutputFormatter; - _newtonsoftJsonOutputFormatter = newtonsoftJsonOutputFormatter; - SupportedEncodings.Add(Encoding.UTF8); SupportedEncodings.Add(Encoding.Unicode); @@ -24,19 +19,24 @@ public class AbpHybridJsonOutputFormatter : TextOutputFormatter SupportedMediaTypes.Add(MediaTypeHeaderValues.ApplicationAnyJsonSyntax); } - public override async Task WriteResponseBodyAsync(OutputFormatterWriteContext context, Encoding selectedEncoding) + public async override Task WriteResponseBodyAsync(OutputFormatterWriteContext context, Encoding selectedEncoding) { - await GetTextInputFormatter(context).WriteResponseBodyAsync(context, selectedEncoding); + await (await GetTextInputFormatter(context)).WriteResponseBodyAsync(context, selectedEncoding); } - protected virtual TextOutputFormatter GetTextInputFormatter(OutputFormatterWriteContext context) + protected virtual async Task GetTextInputFormatter(OutputFormatterWriteContext context) { - var typesMatcher = context.HttpContext.RequestServices.GetRequiredService(); - if (!typesMatcher.Match(context.ObjectType)) + var options = context.HttpContext.RequestServices.GetRequiredService>().Value; + + foreach (var outputFormatterType in options.TextOutputFormatters) { - return _systemTextJsonOutputFormatter; + var outputFormatter = context.HttpContext.RequestServices.GetRequiredService(outputFormatterType).As(); + if (await outputFormatter.CanHandleAsync(context.ObjectType)) + { + return await outputFormatter.GetTextOutputFormatterAsync(); + } } - return _newtonsoftJsonOutputFormatter; + throw new AbpException($"The {nameof(AbpHybridJsonOutputFormatter)} can't handle '{context.ObjectType.GetFullNameWithAssemblyName()}'!"); } } diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/Json/AbpSystemTextJsonFormatter.cs b/framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/Json/AbpSystemTextJsonFormatter.cs new file mode 100644 index 0000000000..e680d54fa5 --- /dev/null +++ b/framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/Json/AbpSystemTextJsonFormatter.cs @@ -0,0 +1,60 @@ +using System; +using System.Text.Encodings.Web; +using System.Text.Json; +using System.Threading.Tasks; +using Microsoft.AspNetCore.Mvc; +using Microsoft.AspNetCore.Mvc.Formatters; +using Microsoft.Extensions.Logging; +using Microsoft.Extensions.Options; +using Volo.Abp.DependencyInjection; +using Volo.Abp.Json.SystemTextJson; + +namespace Volo.Abp.AspNetCore.Mvc.Json; + +public class AbpSystemTextJsonFormatter : IAbpHybridJsonInputFormatter, IAbpHybridJsonOutputFormatter, ITransientDependency +{ + private readonly AbpSystemTextJsonUnsupportedTypeMatcher _unsupportedTypeMatcher; + private readonly IOptions _jsonOptions; + private readonly ILoggerFactory _loggerFactory; + + public AbpSystemTextJsonFormatter(AbpSystemTextJsonUnsupportedTypeMatcher unsupportedTypeMatcher, + IOptions jsonOptions, + ILoggerFactory loggerFactory) + { + _unsupportedTypeMatcher = unsupportedTypeMatcher; + _jsonOptions = jsonOptions; + _loggerFactory = loggerFactory; + } + + Task IAbpHybridJsonInputFormatter.CanHandleAsync(Type type) + { + return Task.FromResult(!_unsupportedTypeMatcher.Match(type)); + } + + public virtual Task GetTextInputFormatterAsync() + { + return Task.FromResult(new SystemTextJsonInputFormatter( + _jsonOptions.Value, + _loggerFactory.CreateLogger())); + } + + Task IAbpHybridJsonOutputFormatter.CanHandleAsync(Type type) + { + return Task.FromResult(!_unsupportedTypeMatcher.Match(type)); + } + + public Task GetTextOutputFormatterAsync() + { + var jsonSerializerOptions = _jsonOptions.Value.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 = new JsonSerializerOptions(jsonSerializerOptions) + { + Encoder = JavaScriptEncoder.UnsafeRelaxedJsonEscaping, + }; + } + + return Task.FromResult(new SystemTextJsonOutputFormatter(jsonSerializerOptions)); + } +} diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/Json/IAbpHybridJsonInputFormatter.cs b/framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/Json/IAbpHybridJsonInputFormatter.cs new file mode 100644 index 0000000000..0a167e58d4 --- /dev/null +++ b/framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/Json/IAbpHybridJsonInputFormatter.cs @@ -0,0 +1,12 @@ +using System; +using System.Threading.Tasks; +using Microsoft.AspNetCore.Mvc.Formatters; + +namespace Volo.Abp.AspNetCore.Mvc.Json; + +public interface IAbpHybridJsonInputFormatter +{ + Task CanHandleAsync(Type type); + + Task GetTextInputFormatterAsync(); +} diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/Json/IAbpHybridJsonOutputFormatter.cs b/framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/Json/IAbpHybridJsonOutputFormatter.cs new file mode 100644 index 0000000000..06418b88d4 --- /dev/null +++ b/framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/Json/IAbpHybridJsonOutputFormatter.cs @@ -0,0 +1,12 @@ +using System; +using System.Threading.Tasks; +using Microsoft.AspNetCore.Mvc.Formatters; + +namespace Volo.Abp.AspNetCore.Mvc.Json; + +public interface IAbpHybridJsonOutputFormatter +{ + Task CanHandleAsync(Type type); + + Task GetTextOutputFormatterAsync(); +} diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/Json/MvcCoreBuilderExtensions.cs b/framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/Json/MvcCoreBuilderExtensions.cs index 2d3a834d09..39a4e0670a 100644 --- a/framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/Json/MvcCoreBuilderExtensions.cs +++ b/framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/Json/MvcCoreBuilderExtensions.cs @@ -1,9 +1,8 @@ using Microsoft.AspNetCore.Mvc; +using Microsoft.AspNetCore.Mvc.Formatters; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection.Extensions; using Microsoft.Extensions.Options; -using Microsoft.Extensions.ObjectPool; -using Volo.Abp.Json; namespace Volo.Abp.AspNetCore.Mvc.Json; @@ -11,18 +10,23 @@ public static class MvcCoreBuilderExtensions { public static IMvcCoreBuilder AddAbpHybridJson(this IMvcCoreBuilder builder) { - var abpJsonOptions = builder.Services.ExecutePreConfiguredActions(); - if (!abpJsonOptions.UseHybridSerializer) + builder.Services.TryAddEnumerable(ServiceDescriptor.Transient, AbpJsonOptionsSetup>()); + + builder.Services.Configure(options => { - builder.Services.TryAddEnumerable(ServiceDescriptor.Transient, AbpMvcNewtonsoftJsonOptionsSetup>()); - builder.AddNewtonsoftJson(); - return builder; - } + options.InputFormatters.RemoveType(); + options.InputFormatters.Add(new AbpHybridJsonInputFormatter()); + + options.OutputFormatters.RemoveType(); + options.OutputFormatters.Add(new AbpHybridJsonOutputFormatter()); + }); + + builder.Services.Configure(options => + { + options.TextInputFormatters.Add(); + options.TextOutputFormatters.Add(); + }); - builder.Services.TryAddTransient(); - builder.Services.TryAddEnumerable(ServiceDescriptor.Transient, AbpJsonOptionsSetup>()); - builder.Services.TryAddEnumerable(ServiceDescriptor.Transient, AbpMvcNewtonsoftJsonOptionsSetup>()); - builder.Services.TryAddEnumerable(ServiceDescriptor.Transient, AbpHybridJsonOptionsSetup>()); return builder; } } diff --git a/framework/src/Volo.Abp.Json.Core/Volo/Abp/Json/AbpJsonOptions.cs b/framework/src/Volo.Abp.Json.Core/Volo/Abp/Json/AbpJsonOptions.cs index 73b1d4506d..ffbd510b93 100644 --- a/framework/src/Volo.Abp.Json.Core/Volo/Abp/Json/AbpJsonOptions.cs +++ b/framework/src/Volo.Abp.Json.Core/Volo/Abp/Json/AbpJsonOptions.cs @@ -9,17 +9,10 @@ public class AbpJsonOptions /// public string DefaultDateTimeFormat { get; set; } - /// - /// It will try to use System.Json.Text to handle JSON if it can otherwise use Newtonsoft. - /// Affects both AbpJsonModule and AbpAspNetCoreMvcModule. - /// - public bool UseHybridSerializer { get; set; } - public ITypeList Providers { get; } public AbpJsonOptions() { Providers = new TypeList(); - UseHybridSerializer = true; } } diff --git a/framework/src/Volo.Abp.Json.SystemTextJson/Volo/Abp/Json/SystemTextJson/AbpJsonSystemTextJsonModule.cs b/framework/src/Volo.Abp.Json.SystemTextJson/Volo/Abp/Json/SystemTextJson/AbpJsonSystemTextJsonModule.cs index 4b15408b51..895c9bcfe4 100644 --- a/framework/src/Volo.Abp.Json.SystemTextJson/Volo/Abp/Json/SystemTextJson/AbpJsonSystemTextJsonModule.cs +++ b/framework/src/Volo.Abp.Json.SystemTextJson/Volo/Abp/Json/SystemTextJson/AbpJsonSystemTextJsonModule.cs @@ -11,16 +11,11 @@ public class AbpJsonSystemTextJsonModule : AbpModule { public override void ConfigureServices(ServiceConfigurationContext context) { - context.Services.TryAddEnumerable(ServiceDescriptor - .Transient, AbpSystemTextJsonSerializerOptionsSetup>()); + context.Services.TryAddEnumerable(ServiceDescriptor.Transient, AbpSystemTextJsonSerializerOptionsSetup>()); - var preActions = context.Services.GetPreConfigureActions(); Configure(options => { - if (preActions.Configure().UseHybridSerializer) - { - options.Providers.Add(); - } + options.Providers.Add(); }); } } diff --git a/framework/test/Volo.Abp.AspNetCore.Mvc.Tests/Volo.Abp.AspNetCore.Mvc.Tests.csproj b/framework/test/Volo.Abp.AspNetCore.Mvc.Tests/Volo.Abp.AspNetCore.Mvc.Tests.csproj index 53a9bd3747..f577746665 100644 --- a/framework/test/Volo.Abp.AspNetCore.Mvc.Tests/Volo.Abp.AspNetCore.Mvc.Tests.csproj +++ b/framework/test/Volo.Abp.AspNetCore.Mvc.Tests/Volo.Abp.AspNetCore.Mvc.Tests.csproj @@ -25,7 +25,7 @@ - + diff --git a/framework/test/Volo.Abp.AspNetCore.Mvc.Tests/Volo/Abp/AspNetCore/Mvc/AbpAspNetCoreMvcTestModule.cs b/framework/test/Volo.Abp.AspNetCore.Mvc.Tests/Volo/Abp/AspNetCore/Mvc/AbpAspNetCoreMvcTestModule.cs index e303a0fb5d..76a95a09cf 100644 --- a/framework/test/Volo.Abp.AspNetCore.Mvc.Tests/Volo/Abp/AspNetCore/Mvc/AbpAspNetCoreMvcTestModule.cs +++ b/framework/test/Volo.Abp.AspNetCore.Mvc.Tests/Volo/Abp/AspNetCore/Mvc/AbpAspNetCoreMvcTestModule.cs @@ -11,6 +11,7 @@ using Volo.Abp.AspNetCore.Mvc.Authorization; using Volo.Abp.AspNetCore.Mvc.GlobalFeatures; using Volo.Abp.AspNetCore.Mvc.Localization; using Volo.Abp.AspNetCore.Mvc.Localization.Resource; +using Volo.Abp.AspNetCore.Mvc.NewtonsoftJson; using Volo.Abp.AspNetCore.Security.Claims; using Volo.Abp.AspNetCore.TestBase; using Volo.Abp.Authorization; @@ -29,7 +30,7 @@ namespace Volo.Abp.AspNetCore.Mvc; [DependsOn( typeof(AbpAspNetCoreTestBaseModule), typeof(AbpMemoryDbTestModule), - typeof(AbpAspNetCoreMvcModule), + typeof(AbpAspNetCoreMvcNewtonsoftModule), typeof(AbpAutofacModule) )] public class AbpAspNetCoreMvcTestModule : AbpModule diff --git a/nupkg/common.ps1 b/nupkg/common.ps1 index b39e3b1ec2..9f44b11c6d 100644 --- a/nupkg/common.ps1 +++ b/nupkg/common.ps1 @@ -112,6 +112,7 @@ $projects = ( "framework/src/Volo.Abp.AspNetCore.Mvc.Client.Common", "framework/src/Volo.Abp.AspNetCore.Mvc.Contracts", "framework/src/Volo.Abp.AspNetCore.Mvc", + "framework/src/Volo.Abp.AspNetCore.Mvc.NewtonsoftJson", "framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap", "framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bundling.Abstractions", "framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bundling",