如何在 ASP.NET Core 6 中使用 DateOnly/TimeOnly 查询参数?
How can I use DateOnly/TimeOnly query parameters in ASP.NET Core 6?
As of .NET 6 in ASP.NET API,如果你想获取DateOnly
(或TimeOnly
)作为查询参数,你需要单独指定所有它是字段,而不是像 DateTime
.
那样只提供字符串(TimeOnly
的“2021-09-14”或“10:54:53”)
如果它们是正文的一部分,我可以通过添加自定义 JSON 转换器 (AddJsonOptions(o => o.JsonSerializerOptions.Converters.Add(...))
) 来解决这个问题,但它不适用于查询参数。
我知道可以使用模型绑定器解决这个问题,但我不想为每个包含 DateOnly/TimeOnly
的模型创建一个模型绑定器。有没有办法在整个应用程序范围内修复此应用程序?
演示:
假设您有一个跟随动作:
[HttpGet] public void Foo([FromQuery] DateOnly date, [FromQuery] TimeOnly time, [FromQuery] DateTime dateTime)
这是它在 Swagger 中的表示方式:
我希望它表示为三个字符串字段:一个用于 DateOnly
,一个用于 TimeOnly
,一个用于 DateTime
(这个已经存在)。
PS:这不是 Swagger 问题,是 ASP.NET 问题。如果我尝试手动传递 ?date=2021-09-14
,ASP.NET 将无法理解。
我这边也遇到了你的问题,好像构造函数本身不支持无参数模式。如以下代码:
public DateOnly(int year, int month, int day)
{
throw null;
}
[NullableContext(1)]
public DateOnly(int year, int month, int day, Calendar calendar)
{
throw null;
}
虽然日期时间支持:
public DateTime Date
{
get
{
throw null;
}
}
因此,恐怕在更新DateOnly
和TimeOnly
之前,我们都需要用string
代替,将字符串分成年月日,然后new DateOnly(int year, int month, int day)
.
事实证明,有两种解决方案:
- 自定义ModelBinder +
- 自定义TypeConverter(Swagger会自动看到,并相应更新UI)
我选择了 TypeConverter
,一切正常!自从 .Net 团队 are not planning to add full support for DateOnly/TimeOnly
in .Net 6,我决定创建一个 NuGet 来这样做:
https://www.nuget.org/packages/DateOnlyTimeOnly.AspNet (source code)
将其添加到项目并按照描述配置 Program.cs
后,问题描述中描述的动作的 Swagger 将如下所示:
它是如何工作的
首先您需要声明从 string
到 DateOnly
的类型转换器(以及一个从 string
到 TimeOnly
的类型转换器):
using System.ComponentModel;
using System.Globalization;
namespace DateOnlyTimeOnly.AspNet.Converters;
public class DateOnlyTypeConverter : TypeConverter
{
public override bool CanConvertFrom(ITypeDescriptorContext? context, Type sourceType)
{
if (sourceType == typeof(string))
{
return true;
}
return base.CanConvertFrom(context, sourceType);
}
public override object? ConvertFrom(ITypeDescriptorContext? context, CultureInfo? culture, object value)
{
if (value is string str)
{
return DateOnly.Parse(str);
}
return base.ConvertFrom(context, culture, value);
}
public override bool CanConvertTo(ITypeDescriptorContext? context, Type? destinationType)
{
if (destinationType == typeof(string))
{
return true;
}
return base.CanConvertTo(context, destinationType);
}
public override object? ConvertTo(ITypeDescriptorContext? context, CultureInfo? culture, object? value, Type destinationType)
{
if (destinationType == typeof(string) && value is DateOnly date)
{
return date.ToString("O");
}
return base.ConvertTo(context, culture, value, destinationType);
}
}
(DateOnly
的一个是一样的,只是DateOnly
换成了TimeOnly
)
比TypeConverterAttribute
需要在DateOnly
和TimeOnly
上添加。可以这样做:
TypeDescriptor.AddAttributes(typeof(DateOnly), new TypeConverterAttribute(typeof(DateOnlyTypeConverter)));
TypeDescriptor.AddAttributes(typeof(TimeOnly), new TypeConverterAttribute(typeof(TimeOnlyTypeConverter)));
为了让它更简洁一些,可以将此代码包装在扩展方法中:
using DateOnlyTimeOnly.AspNet.Converters;
using Microsoft.AspNetCore.Mvc;
using System.ComponentModel;
namespace Microsoft.Extensions.DependencyInjection;
public static class MvcOptionsExtensions
{
public static MvcOptions UseDateOnlyTimeOnlyStringConverters(this MvcOptions options)
{
TypeDescriptor.AddAttributes(typeof(DateOnly), new TypeConverterAttribute(typeof(DateOnlyTypeConverter)));
TypeDescriptor.AddAttributes(typeof(TimeOnly), new TypeConverterAttribute(typeof(TimeOnlyTypeConverter)));
return options;
}
}
用法:
builder.Services.AddControllers(options => options.UseDateOnlyTimeOnlyStringConverters())
As of .NET 6 in ASP.NET API,如果你想获取DateOnly
(或TimeOnly
)作为查询参数,你需要单独指定所有它是字段,而不是像 DateTime
.
TimeOnly
的“2021-09-14”或“10:54:53”)
如果它们是正文的一部分,我可以通过添加自定义 JSON 转换器 (AddJsonOptions(o => o.JsonSerializerOptions.Converters.Add(...))
) 来解决这个问题,但它不适用于查询参数。
我知道可以使用模型绑定器解决这个问题,但我不想为每个包含 DateOnly/TimeOnly
的模型创建一个模型绑定器。有没有办法在整个应用程序范围内修复此应用程序?
演示:
假设您有一个跟随动作:
[HttpGet] public void Foo([FromQuery] DateOnly date, [FromQuery] TimeOnly time, [FromQuery] DateTime dateTime)
这是它在 Swagger 中的表示方式:
我希望它表示为三个字符串字段:一个用于 DateOnly
,一个用于 TimeOnly
,一个用于 DateTime
(这个已经存在)。
PS:这不是 Swagger 问题,是 ASP.NET 问题。如果我尝试手动传递 ?date=2021-09-14
,ASP.NET 将无法理解。
我这边也遇到了你的问题,好像构造函数本身不支持无参数模式。如以下代码:
public DateOnly(int year, int month, int day)
{
throw null;
}
[NullableContext(1)]
public DateOnly(int year, int month, int day, Calendar calendar)
{
throw null;
}
虽然日期时间支持:
public DateTime Date
{
get
{
throw null;
}
}
因此,恐怕在更新DateOnly
和TimeOnly
之前,我们都需要用string
代替,将字符串分成年月日,然后new DateOnly(int year, int month, int day)
.
事实证明,有两种解决方案:
- 自定义ModelBinder +
- 自定义TypeConverter(Swagger会自动看到,并相应更新UI)
我选择了 TypeConverter
,一切正常!自从 .Net 团队 are not planning to add full support for DateOnly/TimeOnly
in .Net 6,我决定创建一个 NuGet 来这样做:
https://www.nuget.org/packages/DateOnlyTimeOnly.AspNet (source code)
将其添加到项目并按照描述配置 Program.cs
后,问题描述中描述的动作的 Swagger 将如下所示:
它是如何工作的
首先您需要声明从 string
到 DateOnly
的类型转换器(以及一个从 string
到 TimeOnly
的类型转换器):
using System.ComponentModel;
using System.Globalization;
namespace DateOnlyTimeOnly.AspNet.Converters;
public class DateOnlyTypeConverter : TypeConverter
{
public override bool CanConvertFrom(ITypeDescriptorContext? context, Type sourceType)
{
if (sourceType == typeof(string))
{
return true;
}
return base.CanConvertFrom(context, sourceType);
}
public override object? ConvertFrom(ITypeDescriptorContext? context, CultureInfo? culture, object value)
{
if (value is string str)
{
return DateOnly.Parse(str);
}
return base.ConvertFrom(context, culture, value);
}
public override bool CanConvertTo(ITypeDescriptorContext? context, Type? destinationType)
{
if (destinationType == typeof(string))
{
return true;
}
return base.CanConvertTo(context, destinationType);
}
public override object? ConvertTo(ITypeDescriptorContext? context, CultureInfo? culture, object? value, Type destinationType)
{
if (destinationType == typeof(string) && value is DateOnly date)
{
return date.ToString("O");
}
return base.ConvertTo(context, culture, value, destinationType);
}
}
(DateOnly
的一个是一样的,只是DateOnly
换成了TimeOnly
)
比TypeConverterAttribute
需要在DateOnly
和TimeOnly
上添加。可以这样做:
TypeDescriptor.AddAttributes(typeof(DateOnly), new TypeConverterAttribute(typeof(DateOnlyTypeConverter)));
TypeDescriptor.AddAttributes(typeof(TimeOnly), new TypeConverterAttribute(typeof(TimeOnlyTypeConverter)));
为了让它更简洁一些,可以将此代码包装在扩展方法中:
using DateOnlyTimeOnly.AspNet.Converters;
using Microsoft.AspNetCore.Mvc;
using System.ComponentModel;
namespace Microsoft.Extensions.DependencyInjection;
public static class MvcOptionsExtensions
{
public static MvcOptions UseDateOnlyTimeOnlyStringConverters(this MvcOptions options)
{
TypeDescriptor.AddAttributes(typeof(DateOnly), new TypeConverterAttribute(typeof(DateOnlyTypeConverter)));
TypeDescriptor.AddAttributes(typeof(TimeOnly), new TypeConverterAttribute(typeof(TimeOnlyTypeConverter)));
return options;
}
}
用法:
builder.Services.AddControllers(options => options.UseDateOnlyTimeOnlyStringConverters())