Refactor & add more unit tests.

pull/5952/head
maliming 5 years ago
parent dee8f22055
commit 74affe2a74

@ -25,7 +25,7 @@ namespace Volo.Abp.AspNetCore.Mvc.Json
protected virtual TextInputFormatter GetTextInputFormatter(InputFormatterContext context)
{
var typesMatcher = context.HttpContext.RequestServices.GetRequiredService<SystemTextJsonSupportTypeMatcher>();
var typesMatcher = context.HttpContext.RequestServices.GetRequiredService<AbpSystemTextJsonSupportTypeMatcher>();
if (typesMatcher.Match(context.ModelType))
{
return context.HttpContext.RequestServices.GetRequiredService<SystemTextJsonInputFormatter>();

@ -25,7 +25,7 @@ namespace Volo.Abp.AspNetCore.Mvc.Json
protected virtual TextOutputFormatter GetTextInputFormatter(OutputFormatterWriteContext context)
{
var typesMatcher = context.HttpContext.RequestServices.GetRequiredService<SystemTextJsonSupportTypeMatcher>();
var typesMatcher = context.HttpContext.RequestServices.GetRequiredService<AbpSystemTextJsonSupportTypeMatcher>();
if (typesMatcher.Match(context.ObjectType))
{
return context.HttpContext.RequestServices.GetRequiredService<SystemTextJsonOutputFormatter>();

@ -23,6 +23,7 @@ namespace Volo.Abp.AspNetCore.Mvc.Json
return new SystemTextJsonInputFormatter(jsonOptions.Value, logger);
});
builder.Services.AddTransient<DefaultObjectPoolProvider>();
//NewtonsoftJsonInputFormatter
builder.Services.AddTransient(provider =>
{

@ -19,13 +19,8 @@ namespace Volo.Abp.Json
Configure<AbpJsonOptions>(options =>
{
options.Providers.Add<NewtonsoftJsonSerializerProvider>();
options.Providers.Add<SystemTextJsonSerializerProvider>();
});
Configure<SystemTextJsonSupportTypeMatcherOptions>(options =>
{
options.UnsupportedAttributes.Add<DisableDateTimeNormalizationAttribute>();
options.Providers.Add<AbpNewtonsoftJsonSerializerProvider>();
options.Providers.Add<AbpSystemTextJsonSerializerProvider>();
});
Configure<AbpNewtonsoftJsonSerializerOptions>(options =>

@ -9,14 +9,14 @@ using Volo.Abp.DependencyInjection;
namespace Volo.Abp.Json.Newtonsoft
{
public class NewtonsoftJsonSerializerProvider : IJsonSerializerProvider, ITransientDependency
public class AbpNewtonsoftJsonSerializerProvider : IJsonSerializerProvider, ITransientDependency
{
private static readonly CamelCaseExceptDictionaryKeysResolver SharedCamelCaseExceptDictionaryKeysResolver =
new CamelCaseExceptDictionaryKeysResolver();
protected List<JsonConverter> Converters { get; }
public NewtonsoftJsonSerializerProvider(
public AbpNewtonsoftJsonSerializerProvider(
IOptions<AbpNewtonsoftJsonSerializerOptions> options,
IServiceProvider serviceProvider)
{

@ -5,23 +5,23 @@ using Volo.Abp.DependencyInjection;
namespace Volo.Abp.Json.SystemTextJson
{
public class SystemTextJsonSerializerProvider : IJsonSerializerProvider, ITransientDependency
public class AbpSystemTextJsonSerializerProvider : IJsonSerializerProvider, ITransientDependency
{
protected AbpSystemTextJsonSerializerOptions Options { get; }
protected SystemTextJsonSupportTypeMatcher SystemTextJsonSupportTypeMatcher { get; }
protected AbpSystemTextJsonSupportTypeMatcher AbpSystemTextJsonSupportTypeMatcher { get; }
public SystemTextJsonSerializerProvider(
public AbpSystemTextJsonSerializerProvider(
IOptions<AbpSystemTextJsonSerializerOptions> options,
SystemTextJsonSupportTypeMatcher systemTextJsonSupportTypeMatcher)
AbpSystemTextJsonSupportTypeMatcher abpSystemTextJsonSupportTypeMatcher)
{
SystemTextJsonSupportTypeMatcher = systemTextJsonSupportTypeMatcher;
AbpSystemTextJsonSupportTypeMatcher = abpSystemTextJsonSupportTypeMatcher;
Options = options.Value;
}
public bool CanHandle(Type type)
{
return SystemTextJsonSupportTypeMatcher.Match(type);
return AbpSystemTextJsonSupportTypeMatcher.Match(type);
}
public string Serialize(object obj, bool camelCase = true, bool indented = false)

@ -0,0 +1,21 @@
using System;
using Microsoft.Extensions.Options;
using Volo.Abp.DependencyInjection;
namespace Volo.Abp.Json.SystemTextJson
{
public class AbpSystemTextJsonSupportTypeMatcher : ITransientDependency
{
protected AbpSystemTextJsonSupportTypeMatcherOptions Options { get; }
public AbpSystemTextJsonSupportTypeMatcher(IOptions<AbpSystemTextJsonSupportTypeMatcherOptions> options)
{
Options = options.Value;
}
public virtual bool Match(Type type)
{
return !Options.UnsupportedTypes.Contains(type);
}
}
}

@ -0,0 +1,14 @@
using Volo.Abp.Collections;
namespace Volo.Abp.Json.SystemTextJson
{
public class AbpSystemTextJsonSupportTypeMatcherOptions
{
public ITypeList UnsupportedTypes { get; }
public AbpSystemTextJsonSupportTypeMatcherOptions()
{
UnsupportedTypes = new TypeList();
}
}
}

@ -1,182 +0,0 @@
using System;
using System.Collections.Concurrent;
using System.Linq;
using Microsoft.Extensions.Options;
using Volo.Abp.DependencyInjection;
using Volo.Abp.Reflection;
using Volo.Abp.Timing;
namespace Volo.Abp.Json.SystemTextJson
{
public class SystemTextJsonSupportTypeMatcher : ITransientDependency
{
private static readonly ConcurrentBag<Type> SupportedTypesCache = new ConcurrentBag<Type>();
private static readonly ConcurrentBag<Type> UnsupportedTypesCache = new ConcurrentBag<Type>();
private readonly SystemTextJsonSupportTypeMatcherOptions _options;
public SystemTextJsonSupportTypeMatcher(IOptions<SystemTextJsonSupportTypeMatcherOptions> options)
{
_options = options.Value;
}
public bool Match(Type type)
{
if (UnsupportedTypesCache.Contains(type))
{
return false;
}
if (SupportedTypesCache.Contains(type))
{
return true;
}
if (_options.UnsupportedTypes.Any(x => x == type))
{
UnsupportedTypesCache.Add(type);
return false;
}
if (type.IsGenericType)
{
foreach (var genericArgument in type.GetGenericArguments())
{
if (!TypeHelper.IsPrimitiveExtended(genericArgument, includeNullables: true, includeEnums: true))
{
if (!Match(genericArgument))
{
return false;
}
}
else
{
if (_options.UnsupportedTypes.Any(x => x == genericArgument))
{
UnsupportedTypesCache.Add(genericArgument);
return false;
}
}
}
return true;
}
if (type.IsArray)
{
var elementType = type.GetElementType();
if (!TypeHelper.IsPrimitiveExtended(elementType, includeNullables: true, includeEnums: true))
{
if (!Match(elementType))
{
return false;
}
}
else
{
if (_options.UnsupportedTypes.Any(x => x == elementType))
{
UnsupportedTypesCache.Add(elementType);
return false;
}
}
return true;
}
if (TypeHelper.IsPrimitiveExtended(type, includeNullables: true, includeEnums: true))
{
return true;
}
if (type.GetCustomAttributes(true).Any(x => _options.UnsupportedAttributes.Any(a => a == x.GetType())))
{
UnsupportedTypesCache.Add(type);
return false;
}
if (type.DeclaringType != null && type.DeclaringType.GetCustomAttributes(true).Any(x => _options.UnsupportedAttributes.Any(a => a == x.GetType())))
{
UnsupportedTypesCache.Add(type);
return false;
}
foreach (var propertyInfo in type.GetProperties())
{
if (propertyInfo.IsDefined(typeof(DisableDateTimeNormalizationAttribute), true))
{
UnsupportedTypesCache.Add(type);
return false;
}
if (_options.UnsupportedTypes.Any(x => x == propertyInfo.PropertyType))
{
UnsupportedTypesCache.Add(propertyInfo.PropertyType);
return false;
}
if (propertyInfo.PropertyType.IsGenericType)
{
foreach (var genericArgument in propertyInfo.PropertyType.GetGenericArguments())
{
if (!TypeHelper.IsPrimitiveExtended(genericArgument, includeNullables: true, includeEnums: true))
{
if (!Match(genericArgument))
{
return false;
}
}
else
{
if (_options.UnsupportedTypes.Any(x => x == genericArgument))
{
UnsupportedTypesCache.Add(genericArgument);
return false;
}
}
}
}
if (propertyInfo.PropertyType.IsArray)
{
var elementType = propertyInfo.PropertyType.GetElementType();
if (!TypeHelper.IsPrimitiveExtended(elementType, includeNullables: true, includeEnums: true))
{
if (!Match(elementType))
{
return false;
}
}
else
{
if (_options.UnsupportedTypes.Any(x => x == elementType))
{
UnsupportedTypesCache.Add(elementType);
return false;
}
}
}
if (!TypeHelper.IsPrimitiveExtended(propertyInfo.PropertyType, includeNullables: true, includeEnums: true))
{
if (!Match(propertyInfo.PropertyType))
{
return false;
}
}
else
{
if (_options.UnsupportedTypes.Any(x => x == propertyInfo.PropertyType))
{
UnsupportedTypesCache.Add(propertyInfo.PropertyType);
return false;
}
}
}
SupportedTypesCache.Add(type);
return true;
}
}
}

@ -1,18 +0,0 @@
using System;
using Volo.Abp.Collections;
namespace Volo.Abp.Json.SystemTextJson
{
public class SystemTextJsonSupportTypeMatcherOptions
{
public ITypeList<Attribute> UnsupportedAttributes { get; }
public ITypeList UnsupportedTypes { get; }
public SystemTextJsonSupportTypeMatcherOptions()
{
UnsupportedAttributes = new TypeList<Attribute>();
UnsupportedTypes = new TypeList();
}
}
}

@ -39,6 +39,16 @@ namespace Volo.Abp.AspNetCore.Mvc.ModelBinding
input.Time3.Value.Kind.ToString().ToLower() + "_" +
input.InnerModel.Time4.Kind.ToString().ToLower();
}
//JSON input and output.
[HttpPost("ComplexTypeDateTimeKind_JSON")]
public string ComplexTypeDateTimeKind_JSON([FromBody]GetDateTimeKindModel input)
{
return input.Time1.Kind.ToString().ToLower() + "_" +
input.Time2.Kind.ToString().ToLower() + "_" +
input.Time3.Value.Kind.ToString().ToLower() + "_" +
input.InnerModel.Time4.Kind.ToString().ToLower();
}
}
public class GetDateTimeKindModel

@ -1,9 +1,14 @@
using System;
using System.Net;
using System.Net.Http;
using System.Text;
using System.Text.Json;
using System.Threading.Tasks;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Shouldly;
using Volo.Abp.Http;
using Volo.Abp.Json.SystemTextJson;
using Volo.Abp.Timing;
using Xunit;
@ -13,6 +18,15 @@ namespace Volo.Abp.AspNetCore.Mvc.ModelBinding
{
protected DateTimeKind DateTimeKind { get; set; }
protected override void ConfigureServices(HostBuilderContext context, IServiceCollection services)
{
services.Configure<AbpSystemTextJsonSupportTypeMatcherOptions>(options =>
{
options.UnsupportedTypes.Add<GetDateTimeKindModel>();
options.UnsupportedTypes.Add<GetDateTimeKindModel.GetDateTimeKindInnerModel>();
});
}
[Fact]
public async Task DateTimeKind_Test()
{
@ -75,8 +89,29 @@ namespace Volo.Abp.AspNetCore.Mvc.ModelBinding
var resultAsString = await response.Content.ReadAsStringAsync();
//Time parameter(2010-01-01T00:00:00Z) with time zone information, so the default Kind is UTC
//https://docs.microsoft.com/en-us/aspnet/core/migration/31-to-50?view=aspnetcore-3.1&tabs=visual-studio#datetime-values-are-model-bound-as-utc-times
resultAsString.ShouldBe(
$"utc_{DateTimeKind.ToString().ToLower()}_{DateTimeKind.ToString().ToLower()}_utc");
resultAsString.ShouldBe($"utc_{DateTimeKind.ToString().ToLower()}_{DateTimeKind.ToString().ToLower()}_utc");
}
[Fact]
public async Task ComplexTypeDateTimeKind_JSON_Test()
{
var time = DateTime.Parse("2010-01-01T00:00:00Z");
var response = await Client.PostAsync("/api/model-Binding-test/ComplexTypeDateTimeKind_JSON",
new StringContent(JsonSerializer.Serialize(
new GetDateTimeKindModel {
Time1 = time,
Time2 = time,
Time3 = time,
InnerModel = new GetDateTimeKindModel.GetDateTimeKindInnerModel
{
Time4 = time
}
}
), Encoding.UTF8, MimeTypes.Application.Json));
response.StatusCode.ShouldBe(HttpStatusCode.OK);
var resultAsString = await response.Content.ReadAsStringAsync();
resultAsString.ShouldBe($"local_{DateTimeKind.ToString().ToLower()}_{DateTimeKind.ToString().ToLower()}_local");
}
}
@ -86,6 +121,8 @@ namespace Volo.Abp.AspNetCore.Mvc.ModelBinding
{
DateTimeKind = DateTimeKind.Utc;
services.Configure<AbpClockOptions>(x => x.Kind = DateTimeKind);
base.ConfigureServices(context, services);
}
}
@ -95,6 +132,8 @@ namespace Volo.Abp.AspNetCore.Mvc.ModelBinding
{
DateTimeKind = DateTimeKind.Local;
services.Configure<AbpClockOptions>(x => x.Kind = DateTimeKind);
base.ConfigureServices(context, services);
}
}
}

@ -0,0 +1,66 @@
using System;
using System.Collections.Generic;
using Microsoft.Extensions.DependencyInjection;
using Shouldly;
using Volo.Abp.Json.SystemTextJson;
using Xunit;
namespace Volo.Abp.Json
{
public class AbpSystemTextJsonSupportTypeMatcher_Tests : AbpJsonTestBase
{
private readonly AbpSystemTextJsonSupportTypeMatcher _abpSystemTextJsonSupportTypeMatcher;
public AbpSystemTextJsonSupportTypeMatcher_Tests()
{
_abpSystemTextJsonSupportTypeMatcher = GetRequiredService<AbpSystemTextJsonSupportTypeMatcher>();
}
protected override void AfterAddApplication(IServiceCollection services)
{
services.Configure<AbpSystemTextJsonSupportTypeMatcherOptions>(options =>
{
options.UnsupportedTypes.Add<MyClass>();
options.UnsupportedTypes.Add<byte[]>();
options.UnsupportedTypes.Add<Dictionary<string, MyClass4>>();
});
}
[Fact]
public void CanHandle_Test()
{
_abpSystemTextJsonSupportTypeMatcher.Match(typeof(MyClass)).ShouldBeFalse();
_abpSystemTextJsonSupportTypeMatcher.Match(typeof(byte[])).ShouldBeFalse();
_abpSystemTextJsonSupportTypeMatcher.Match(typeof(MyClass2)).ShouldBeTrue();
_abpSystemTextJsonSupportTypeMatcher.Match(typeof(MyClass3)).ShouldBeTrue();
_abpSystemTextJsonSupportTypeMatcher.Match(typeof(MyClass4)).ShouldBeTrue();
_abpSystemTextJsonSupportTypeMatcher.Match(typeof(string)).ShouldBeTrue();
_abpSystemTextJsonSupportTypeMatcher.Match(typeof(string[])).ShouldBeTrue();
_abpSystemTextJsonSupportTypeMatcher.Match(typeof(Dictionary<string, MyClass4>)).ShouldBeFalse();
_abpSystemTextJsonSupportTypeMatcher.Match(typeof(IDictionary<string, MyClass4>)).ShouldBeTrue();
}
class MyClass
{
public DateTime Prop1 { get; set; }
}
class MyClass2
{
public DateTime Prop1 { get; set; }
}
class MyClass3
{
public MyClass4 Prop1 { get; set; }
}
class MyClass4
{
public DateTime Prop1 { get; set; }
}
}
}

@ -1,113 +0,0 @@
using System;
using System.Collections.Generic;
using Microsoft.Extensions.DependencyInjection;
using Shouldly;
using Volo.Abp.Json.SystemTextJson;
using Volo.Abp.Timing;
using Xunit;
namespace Volo.Abp.Json
{
public class SystemTextJsonSupportTypeMatcher_Tests : AbpJsonTestBase
{
private readonly SystemTextJsonSupportTypeMatcher _systemTextJsonSupportTypeMatcher;
protected override void AfterAddApplication(IServiceCollection services)
{
services.Configure<SystemTextJsonSupportTypeMatcherOptions>(options =>
{
options.UnsupportedTypes.Add<MyClass7>();
options.UnsupportedTypes.Add<byte>();
});
base.AfterAddApplication(services);
}
public SystemTextJsonSupportTypeMatcher_Tests()
{
_systemTextJsonSupportTypeMatcher = GetRequiredService<SystemTextJsonSupportTypeMatcher>();
}
[Fact]
public void CanHandle_Test()
{
_systemTextJsonSupportTypeMatcher.Match(typeof(MyClass)).ShouldBeFalse();
_systemTextJsonSupportTypeMatcher.Match(typeof(MyClass2)).ShouldBeFalse();
_systemTextJsonSupportTypeMatcher.Match(typeof(MyClass3)).ShouldBeFalse();
_systemTextJsonSupportTypeMatcher.Match(typeof(MyClass4)).ShouldBeFalse();
_systemTextJsonSupportTypeMatcher.Match(typeof(MyClass5)).ShouldBeTrue();
_systemTextJsonSupportTypeMatcher.Match(typeof(MyClass6)).ShouldBeTrue();
_systemTextJsonSupportTypeMatcher.Match(typeof(MyClass7)).ShouldBeFalse();
_systemTextJsonSupportTypeMatcher.Match(typeof(MyClass8)).ShouldBeFalse();
_systemTextJsonSupportTypeMatcher.Match(typeof(MyClass9)).ShouldBeFalse();
_systemTextJsonSupportTypeMatcher.Match(typeof(string)).ShouldBeTrue();
_systemTextJsonSupportTypeMatcher.Match(typeof(string[])).ShouldBeTrue();
_systemTextJsonSupportTypeMatcher.Match(typeof(int)).ShouldBeTrue();
_systemTextJsonSupportTypeMatcher.Match(typeof(byte)).ShouldBeFalse();
_systemTextJsonSupportTypeMatcher.Match(typeof(Dictionary<byte, byte>)).ShouldBeFalse();
_systemTextJsonSupportTypeMatcher.Match(typeof(Dictionary<string, MyClass10>)).ShouldBeFalse();
}
[DisableDateTimeNormalization]
class MyClass
{
public DateTime Prop1 { get; set; }
}
class MyClass2
{
[DisableDateTimeNormalization]
public DateTime Prop1 { get; set; }
}
class MyClass3
{
public MyClass4 Prop1 { get; set; }
}
class MyClass4
{
[DisableDateTimeNormalization]
public DateTime Prop1 { get; set; }
}
class MyClass5
{
public DateTime Prop1 { get; set; }
public MyClass6 Prop2 { get; set; }
}
class MyClass6
{
public DateTime Prop1 { get; set; }
}
class MyClass7
{
public DateTime Prop1 { get; set; }
}
class MyClass8
{
public MyClass10[] Prop1 { get; set; }
}
class MyClass9
{
public Dictionary<string, MyClass10> Prop1 { get; set; }
}
class MyClass10
{
[DisableDateTimeNormalization]
public DateTime Prop1 { get; set; }
}
}
}
Loading…
Cancel
Save