如何在 ASP.NET Core 6 中使用 DateOnly/TimeOnly 查询参数?

Мак*_*вой 33 c# typeconverter model-binding asp.net-core-webapi .net-6.0

从 ASP.NET API 中的 .NET 6 开始,如果要获取DateOnly(或TimeOnly) 作为查询参数,则需要单独指定其所有字段,而不是仅提供字符串(“2021-09-14”或“10: 54:53" 为TimeOnly) 就像你可以为 一样DateTime

如果它们是正文的一部分,我可以通过添加自定义 JSON 转换器 ( AddJsonOptions(o => o.JsonSerializerOptions.Converters.Add(...))) 来解决这个问题,但它不适用于查询参数。

我知道这可以通过模型绑定器修复,但我不想为每个包含DateOnly/TimeOnly. 有没有办法广泛修复这个应用程序?

演示:

假设您有以下操作:

[HttpGet] public void Foo([FromQuery] DateOnly date, [FromQuery] TimeOnly time, [FromQuery] DateTime dateTime)

下面是它在 Swagger 中的表示方式:

在此输入图像描述

我希望它表示为三个字符串字段:一个 for DateOnly、一个 forTimeOnly和一个 for DateTime(这个已经存在)。

PS:这不是 Swagger 问题,而是 ASP.NET 问题。如果我尝试?date=2021-09-14手动传递,ASP.NET 将无法理解它。

Мак*_*вой 46

事实证明,有两种解决方案:

我和 一起去了TypeConverter,一切正常!由于 .Net 团队不打算DateOnly/TimeOnly在 .Net 6 中添加完全支持,因此我决定创建一个 NuGet 来执行此操作:

https://www.nuget.org/packages/DateOnlyTimeOnly.AspNet源代码

将其添加到项目并Program.cs按照描述进行配置后,问题描述中描述的操作的 Swagger 将如下所示:

在此输入图像描述

它是如何工作的

首先,您需要声明类型转换器 from stringto DateOnly(以及一个 from stringto 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);
    }
}
Run Code Online (Sandbox Code Playgroud)

(其中一个 forDateOnly是相同的,但DateOnly被替换为TimeOnly

需要TypeConverterAttribute添加DateOnlyTimeOnly。可以这样做:

TypeDescriptor.AddAttributes(typeof(DateOnly), new TypeConverterAttribute(typeof(DateOnlyTypeConverter)));
TypeDescriptor.AddAttributes(typeof(TimeOnly), new TypeConverterAttribute(typeof(TimeOnlyTypeConverter)));
Run Code Online (Sandbox Code Playgroud)

为了使其更简洁,可以将这段代码包装在扩展方法中:

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;
    }
}
Run Code Online (Sandbox Code Playgroud)

用法:

builder.Services.AddControllers(options => options.UseDateOnlyTimeOnlyStringConverters())
Run Code Online (Sandbox Code Playgroud)

  • @AlexKlaus 是的,我知道。这是我的包裹,答案的第三段引用了它。很高兴您发现它有帮助 (8认同)
  • 您应该解释解决方案并在答案本身中发布代码 - 类型转换器的代码以及如何将其添加到 MvcOptions 中。DateOnly 转换器的代码可以轻松地适应答案 (4认同)
  • 对于那些仅根据此信息设置有点困难的人(像我一样),请查看该项目的 GitHub 页面:https://github.com/maxkoshevoi/DateOnlyTimeOnly.AspNet (2认同)