mirror of https://github.com/abpframework/abp.git
committed by
GitHub
5 changed files with 259 additions and 1 deletions
@ -0,0 +1,49 @@ |
|||||
|
using System; |
||||
|
using System.Threading.Tasks; |
||||
|
using Microsoft.AspNetCore.Mvc.ModelBinding; |
||||
|
using Microsoft.AspNetCore.Mvc.ModelBinding.Binders; |
||||
|
using Microsoft.Extensions.DependencyInjection; |
||||
|
using Microsoft.Extensions.Logging; |
||||
|
using Volo.Abp.Timing; |
||||
|
|
||||
|
namespace Volo.Abp.AspNetCore.Mvc.ModelBinding |
||||
|
{ |
||||
|
public class AbpDateTimeModelBinder : IModelBinder |
||||
|
{ |
||||
|
private readonly Type _type; |
||||
|
private readonly SimpleTypeModelBinder _simpleTypeModelBinder; |
||||
|
private readonly IClock _clock; |
||||
|
|
||||
|
public AbpDateTimeModelBinder(ModelBinderProviderContext context) |
||||
|
{ |
||||
|
_type = context.Metadata.ModelType; |
||||
|
_clock = context.Services.GetRequiredService<IClock>(); |
||||
|
_simpleTypeModelBinder = new SimpleTypeModelBinder(context.Metadata.ModelType, |
||||
|
context.Services.GetRequiredService<ILoggerFactory>()); |
||||
|
} |
||||
|
|
||||
|
public async Task BindModelAsync(ModelBindingContext bindingContext) |
||||
|
{ |
||||
|
await _simpleTypeModelBinder.BindModelAsync(bindingContext); |
||||
|
|
||||
|
if (!bindingContext.Result.IsModelSet) |
||||
|
{ |
||||
|
return; |
||||
|
} |
||||
|
|
||||
|
if (_type == typeof(DateTime)) |
||||
|
{ |
||||
|
var dateTime = (DateTime) bindingContext.Result.Model; |
||||
|
bindingContext.Result = ModelBindingResult.Success(_clock.Normalize(dateTime)); |
||||
|
} |
||||
|
else |
||||
|
{ |
||||
|
var dateTime = (DateTime?) bindingContext.Result.Model; |
||||
|
if (dateTime != null) |
||||
|
{ |
||||
|
bindingContext.Result = ModelBindingResult.Success(_clock.Normalize(dateTime.Value)); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,47 @@ |
|||||
|
using System; |
||||
|
using System.Linq; |
||||
|
using Microsoft.AspNetCore.Mvc.ModelBinding; |
||||
|
using Microsoft.AspNetCore.Mvc.ModelBinding.Metadata; |
||||
|
using Volo.Abp.Timing; |
||||
|
|
||||
|
namespace Volo.Abp.AspNetCore.Mvc.ModelBinding |
||||
|
{ |
||||
|
public class AbpDateTimeModelBinderProvider : IModelBinderProvider |
||||
|
{ |
||||
|
public IModelBinder GetBinder(ModelBinderProviderContext context) |
||||
|
{ |
||||
|
if (context.Metadata.ModelType != typeof(DateTime) && |
||||
|
context.Metadata.ModelType != typeof(DateTime?)) |
||||
|
{ |
||||
|
return null; |
||||
|
} |
||||
|
|
||||
|
if (context.Metadata.ContainerType == null) |
||||
|
{ |
||||
|
if (context.Metadata is DefaultModelMetadata defaultModelMetadata && |
||||
|
defaultModelMetadata.Attributes.Attributes.All(x => x.GetType() != typeof(DisableDateTimeNormalizationAttribute))) |
||||
|
{ |
||||
|
return new AbpDateTimeModelBinder(context); |
||||
|
} |
||||
|
} |
||||
|
else |
||||
|
{ |
||||
|
var dateNormalizationDisabledForClass = |
||||
|
context.Metadata.ContainerType.IsDefined(typeof(DisableDateTimeNormalizationAttribute), true); |
||||
|
|
||||
|
var dateNormalizationDisabledForProperty = context.Metadata.ContainerType |
||||
|
.GetProperty(context.Metadata.PropertyName) |
||||
|
?.IsDefined(typeof(DisableDateTimeNormalizationAttribute), true); |
||||
|
|
||||
|
if (!dateNormalizationDisabledForClass && |
||||
|
dateNormalizationDisabledForProperty != null && |
||||
|
!dateNormalizationDisabledForProperty.Value) |
||||
|
{ |
||||
|
return new AbpDateTimeModelBinder(context); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
return null; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,61 @@ |
|||||
|
using System; |
||||
|
using Microsoft.AspNetCore.Mvc; |
||||
|
using Volo.Abp.Timing; |
||||
|
|
||||
|
namespace Volo.Abp.AspNetCore.Mvc.ModelBinding |
||||
|
{ |
||||
|
[Route("api/model-Binding-test")] |
||||
|
public class ModelBindingController : AbpController |
||||
|
{ |
||||
|
[HttpGet("DateTimeKind")] |
||||
|
public string DateTimeKind(DateTime input) |
||||
|
{ |
||||
|
return input.Kind.ToString().ToLower(); |
||||
|
} |
||||
|
|
||||
|
[HttpGet("NullableDateTimeKind")] |
||||
|
public string NullableDateTimeKind(DateTime? input) |
||||
|
{ |
||||
|
return input.Value.Kind.ToString().ToLower(); |
||||
|
} |
||||
|
|
||||
|
[HttpGet("DisableDateTimeNormalizationDateTimeKind")] |
||||
|
public string DisableDateTimeNormalizationDateTimeKind([DisableDateTimeNormalization]DateTime input) |
||||
|
{ |
||||
|
return input.Kind.ToString().ToLower(); |
||||
|
} |
||||
|
|
||||
|
[HttpGet("DisableDateTimeNormalizationNullableDateTimeKind")] |
||||
|
public string DisableDateTimeNormalizationNullableDateTimeKind([DisableDateTimeNormalization]DateTime? input) |
||||
|
{ |
||||
|
return input.Value.Kind.ToString().ToLower(); |
||||
|
} |
||||
|
|
||||
|
[HttpGet("ComplexTypeDateTimeKind")] |
||||
|
public string ComplexTypeDateTimeKind(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 |
||||
|
{ |
||||
|
[DisableDateTimeNormalization] |
||||
|
public DateTime Time1 { get; set; } |
||||
|
|
||||
|
public DateTime Time2 { get; set; } |
||||
|
|
||||
|
public DateTime? Time3 { get; set; } |
||||
|
|
||||
|
public GetDateTimeKindInnerModel InnerModel { get; set; } |
||||
|
|
||||
|
[DisableDateTimeNormalization] |
||||
|
public class GetDateTimeKindInnerModel |
||||
|
{ |
||||
|
public DateTime Time4 { get; set; } |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,100 @@ |
|||||
|
using System; |
||||
|
using System.Net; |
||||
|
using System.Threading.Tasks; |
||||
|
using Microsoft.Extensions.DependencyInjection; |
||||
|
using Microsoft.Extensions.Hosting; |
||||
|
using Shouldly; |
||||
|
using Volo.Abp.Timing; |
||||
|
using Xunit; |
||||
|
|
||||
|
namespace Volo.Abp.AspNetCore.Mvc.ModelBinding |
||||
|
{ |
||||
|
public abstract class ModelBindingController_Tests : AspNetCoreMvcTestBase |
||||
|
{ |
||||
|
protected DateTimeKind DateTimeKind { get; set; } |
||||
|
|
||||
|
protected override void ConfigureServices(HostBuilderContext context, IServiceCollection services) |
||||
|
{ |
||||
|
services.Configure<AbpClockOptions>(x => x.Kind = DateTimeKind); |
||||
|
} |
||||
|
|
||||
|
[Fact] |
||||
|
public async Task DateTimeKind_Test() |
||||
|
{ |
||||
|
var response = await Client.GetAsync("/api/model-Binding-test/DateTimeKind?input=2010-01-01T00:00:00Z"); |
||||
|
|
||||
|
response.StatusCode.ShouldBe(HttpStatusCode.OK); |
||||
|
var resultAsString = await response.Content.ReadAsStringAsync(); |
||||
|
resultAsString.ShouldBe(DateTimeKind.ToString().ToLower()); |
||||
|
} |
||||
|
|
||||
|
[Fact] |
||||
|
public async Task NullableDateTimeKind_Test() |
||||
|
{ |
||||
|
var response = |
||||
|
await Client.GetAsync("/api/model-Binding-test/NullableDateTimeKind?input=2010-01-01T00:00:00Z"); |
||||
|
|
||||
|
response.StatusCode.ShouldBe(HttpStatusCode.OK); |
||||
|
var resultAsString = await response.Content.ReadAsStringAsync(); |
||||
|
resultAsString.ShouldBe(DateTimeKind.ToString().ToLower()); |
||||
|
} |
||||
|
|
||||
|
[Fact] |
||||
|
public async Task DisableDateTimeNormalizationDateTimeKind_Test() |
||||
|
{ |
||||
|
var response = |
||||
|
await Client.GetAsync( |
||||
|
"/api/model-Binding-test/DisableDateTimeNormalizationDateTimeKind?input=2010-01-01T00:00:00Z"); |
||||
|
|
||||
|
response.StatusCode.ShouldBe(HttpStatusCode.OK); |
||||
|
var resultAsString = await response.Content.ReadAsStringAsync(); |
||||
|
//Time parameter(2010-01-01T00:00:00Z) with time zone information, so the default Kind is Local.
|
||||
|
resultAsString.ShouldBe(DateTimeKind.Local.ToString().ToLower()); |
||||
|
} |
||||
|
|
||||
|
[Fact] |
||||
|
public async Task DisableDateTimeNormalizationNullableDateTimeKind_Test() |
||||
|
{ |
||||
|
var response = |
||||
|
await Client.GetAsync( |
||||
|
"/api/model-Binding-test/DisableDateTimeNormalizationNullableDateTimeKind?input=2010-01-01T00:00:00Z"); |
||||
|
|
||||
|
response.StatusCode.ShouldBe(HttpStatusCode.OK); |
||||
|
var resultAsString = await response.Content.ReadAsStringAsync(); |
||||
|
//Time parameter(2010-01-01T00:00:00Z) with time zone information, so the default Kind is Local.
|
||||
|
resultAsString.ShouldBe(DateTimeKind.Local.ToString().ToLower()); |
||||
|
} |
||||
|
|
||||
|
[Fact] |
||||
|
public async Task ComplexTypeDateTimeKind_Test() |
||||
|
{ |
||||
|
var response = await Client.GetAsync("/api/model-Binding-test/ComplexTypeDateTimeKind?" + |
||||
|
"Time1=2010-01-01T00:00:00Z&" + |
||||
|
"Time2=2010-01-01T00:00:00Z&" + |
||||
|
"Time3=2010-01-01T00:00:00Z&" + |
||||
|
"InnerModel.Time4=2010-01-01T00:00:00Z"); |
||||
|
|
||||
|
response.StatusCode.ShouldBe(HttpStatusCode.OK); |
||||
|
var resultAsString = await response.Content.ReadAsStringAsync(); |
||||
|
//Time parameter(2010-01-01T00:00:00Z) with time zone information, so the default Kind is Local.
|
||||
|
resultAsString.ShouldBe( |
||||
|
$"local_{DateTimeKind.ToString().ToLower()}_{DateTimeKind.ToString().ToLower()}_local"); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
public class ModelBindingController_Utc_Tests : ModelBindingController_Tests |
||||
|
{ |
||||
|
public ModelBindingController_Utc_Tests() |
||||
|
{ |
||||
|
DateTimeKind = DateTimeKind.Utc; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
public class ModelBindingController_Local_Tests : ModelBindingController_Tests |
||||
|
{ |
||||
|
public ModelBindingController_Local_Tests() |
||||
|
{ |
||||
|
DateTimeKind = DateTimeKind.Local; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
Loading…
Reference in new issue