【问题标题】:How can I use DateOnly/TimeOnly query parameters in ASP.NET Core 6?如何在 ASP.NET Core 6 中使用 DateOnly/TimeOnly 查询参数?
【发布时间】:2021-09-15 05:40:54
【问题描述】:

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

如果它们是正文的一部分,我可以通过添加自定义 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 将无法理解。

【问题讨论】:

  • 对不起,我没听清楚,你的意思是你想要一个过滤器来检查 url 中的查询参数,然后如果有单独的年、月、日、小时、分钟、秒, 然后把它们变成日期时间?
  • 对不起,如果我不清楚。 TLDR 或者这是我希望 DateOnlyTimeOnly 由单个字符串参数表示(如 DateTime 目前是)。因此,上面的屏幕截图中应该有三个字段。更新了问题描述。

标签: c# asp.net-core-webapi model-binding .net-6.0 typeconverter


【解决方案1】:

原来,有两种解决方案:

我选择了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 将如下所示:

它是如何工作的

首先,您需要声明从stringDateOnly 的类型转换器(以及从stringTimeOnly 的一个):

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 需要添加到DateOnlyTimeOnly。可以这样做:

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())

【讨论】:

  • 您应该解释解决方案并将代码发布在答案本身中 - 类型转换器的代码以及如何将其添加到 MvcOptions。 DateOnly 转换器的代码很容易适合答案
【解决方案2】:

我也遇到了你的问题,构造函数本身似乎不支持无参数模式。如下代码:

public DateOnly(int year, int month, int day)
        {
            throw null;
        }

        [NullableContext(1)]
        public DateOnly(int year, int month, int day, Calendar calendar)
        {
            throw null;
        }

虽然 DateTime 支持:

public DateTime Date
    {
        get
        {
            throw null;
        }
    }

因此,恐怕在更新DateOnlyTimeOnly 之前,我们都需要改用string 并将字符串分成年、月和日,然后再进行new DateOnly(int year, int month, int day)

【讨论】:

  • 我认为存在一些误解。 1. DateOnlyTimeOnly 都具有从 string 创建它们的 Parse 方法(DateTime 也是如此)。 2. 我看不出DateTime.Date 属性是如何相关的。从字符串构造DateTime 无济于事。无参数构造函数也是如此。如果类没有无参数构造函数,它仍然可以使用自定义序列化逻辑或其他方式进行反灭菌。
  • 是的,我同意你的看法,我们需要进行转换以使字符串变成 DateOnly 或 TimeOnly。
  • @TinyWang 看起来你一直在反编译没有实现的“ref”程序集:dunnhq.com/posts/2021/anatomy-of-a-dotnet-app/…
猜你喜欢
  • 2023-01-11
  • 2021-12-16
  • 1970-01-01
  • 2022-10-13
  • 2022-08-15
  • 2019-02-01
  • 2021-06-13
  • 2021-11-22
  • 2022-07-19
相关资源
最近更新 更多