c# - NSwag:如何在 C# -> Swagger -> C# 客户端中使用自定义值对象类型?

标签 c# swagger swagger-codegen nodatime nswag

我有一个使用 Noda Time 的 API输入和输出类型。这些类型使用默认的 Noda Time 序列化格式(基本上是 ISO-8601 格式)序列化为 JSON 中的字符串。

我有一个看起来像这样的对象:

public class NodaTimeDataStructure
{
    public System.DateTime DateTime { get; set; }
    public DateInterval DateInterval { get; set; }
    public DateTimeZone DateTimeZone { get; set; }
    public Duration Duration { get; set; }
    public Instant Instant { get; set; }
    public Interval Interval { get; set; }
    public IsoDayOfWeek IsoDayOfWeek { get; set; }
    public LocalDate LocalDate { get; set; }
    public LocalDateTime LocalDateTime { get; set; }
    public LocalTime LocalTime { get; set; }
    public Offset Offset { get; set; }
    public OffsetDate OffsetDate { get; set; }
    public OffsetDateTime OffsetDateTime { get; set; }
    public OffsetTime OffsetTime { get; set; }
    public Period Period { get; set; }
    public ZonedDateTime ZonedDateTime { get; set; }
}

这通常会产生以下 Swagger JSON:

"NodaTimeDataStructure": {
  "type": "object",
  "additionalProperties": false,
  "required": [
    "dateTime", "duration", "instant", "interval", "isoDayOfWeek", "localDate", "localDateTime",
    "localTime", "offset", "offsetDate", "offsetDateTime", "offsetTime", "zonedDateTime"
  ],
  "properties": {
    "dateTime":       { "type": "string", "format": "date-time" },
    "instant":        { "type": "string", "format": "date-time" },
    "zonedDateTime":  { "type": "string", "format": "date-time" },
    "offsetDateTime": { "type": "string", "format": "date-time" },
    "localDateTime":  { "type": "string", "format": "date-time" },
    "localDate":      { "type": "string", "format": "date" },
    "localTime":      { "type": "string", "format": "time" },
    "duration":       { "type": "string", "format": "time-span" },
    "dateInterval":   { "type": "array", "items": { "type": "string", "format": "date" } },
    "dateTimeZone":   { "$ref": "#/definitions/DateTimeZone" },
    "interval":       { "$ref": "#/definitions/Interval" },
    "isoDayOfWeek":   { "$ref": "#/definitions/IsoDayOfWeek" },
    "offset":         { "$ref": "#/definitions/Offset" },
    "offsetDate":     { "$ref": "#/definitions/OffsetDate" },
    "offsetTime":     { "$ref": "#/definitions/OffsetTime" },
    "period":         { "$ref": "#/definitions/Period" }
  }
}

这使得无法在 C# 客户端中转换回正确的 Noda Time 类型。除了许多不同的类型具有完全相同的格式 ("date-time") 使得无法进行映射之外,某些类型具有不幸的定义。 DateInterval 生成一个 “date” 数组,因为它是 LocalDate 的可枚举,但简单的开始/结束日期格式会很有效更好的。其他方法是使用 $ref 创建的,用于包含完全不感兴趣的字段的非常复杂的对象。请注意,所有这些都应序列化为简单字符串(可以说不是间隔)。

我可以创建自己的类型映射器并将它们添加到 AspNetCoreToSwaggerGeneratorSettings 中,如下所示:

var nodaTimeTypeMappers = new[]
{
    CreateTypeMapper(typeof(DateInterval), "date-interval"),
    CreateTypeMapper(typeof(DateTimeZone), "date-time-zone"),
    CreateTypeMapper(typeof(Duration), "duration"),
    CreateTypeMapper(typeof(Instant), "instant"),
    CreateTypeMapper(typeof(Interval), "interval"),
    CreateTypeMapper(typeof(IsoDayOfWeek), "iso-day-of-week"),
    CreateTypeMapper(typeof(LocalDate), "local-date"),
    CreateTypeMapper(typeof(LocalDateTime), "local-date-time"),
    CreateTypeMapper(typeof(LocalTime), "local-time"),
    CreateTypeMapper(typeof(Offset), "offset"),
    CreateTypeMapper(typeof(OffsetDate), "offset-date"),
    CreateTypeMapper(typeof(OffsetDateTime), "offset-date-time"),
    CreateTypeMapper(typeof(OffsetTime), "offset-time"),
    CreateTypeMapper(typeof(Period), "period"),
    CreateTypeMapper(typeof(ZonedDateTime), "zoned-date-time"),
};

foreach (var typeMapper in nodaTimeTypeMappers)
{
    settings.TypeMappers.Add(typeMapper);
}

PrimitiveTypeMapper CreateTypeMapper(Type type, string name)
{
    return new PrimitiveTypeMapper(type, s =>
    {
        s.Type = JsonObjectType.String;
        s.Format = "noda-time-" + name;
    });
}

得到这样的东西:

"NodaTimeRequest": {
  "type": "object",
  "additionalProperties": false,
  "required": [
    "dateTime", "duration", "instant", "interval", "isoDayOfWeek", "localDate", "localDateTime",
    "localTime", "offset", "offsetDate", "offsetDateTime", "offsetTime", "zonedDateTime"
  ],
  "properties": {
    "dateTime":       { "type": "string", "format": "date-time" },
    "dateInterval":   { "type": "string", "format": "noda-time-date-interval" },
    "dateTimeZone":   { "type": "string", "format": "noda-time-date-time-zone" },
    "duration":       { "type": "string", "format": "noda-time-duration" },
    "instant":        { "type": "string", "format": "noda-time-instant" },
    "interval":       { "type": "string", "format": "noda-time-interval" },
    "isoDayOfWeek":   { "type": "string", "format": "noda-time-iso-day-of-week" },
    "localDate":      { "type": "string", "format": "noda-time-local-date" },
    "localDateTime":  { "type": "string", "format": "noda-time-local-date-time" },
    "localTime":      { "type": "string", "format": "noda-time-local-time" },
    "offset":         { "type": "string", "format": "noda-time-offset" },
    "offsetDate":     { "type": "string", "format": "noda-time-offset-date" },
    "offsetDateTime": { "type": "string", "format": "noda-time-offset-date-time" },
    "offsetTime":     { "type": "string", "format": "noda-time-offset-time" },
    "period":         { "type": "string", "format": "noda-time-period" },
    "zonedDateTime":  { "type": "string", "format": "noda-time-zoned-date-time" }
  }
}

这允许像现有格式一样使用格式(“date-time”“date”“time” , "time-span"), 但我实在想不出如何让 swagger2csclient 使用这些格式正确地转换回相应的 Noda时间类型。我通常会遗漏什么吗?或者这目前不可能吗?

最佳答案

我没有 Swagger json 问题的解决方案,但我可以帮助解决 C# 客户端生成部分。

不是从 NSwag json 生成客户端,我们要做的是 NSwagStudio使用反射生成客户端。我正在使用“通过反射的 Web API”,并将运行时设置为“默认”:

enter image description here

此生成器“使用 .NET 反射来分析 ASP.NET Web API 或 ASP.NET Core Controller ”。您的里程当然可能会有所不同 - 还有一个“.NET 程序集”选项和/或您可能需要明确设置运行时。

在右侧 Pane 中,单击“CSharp 客户端”并切换到“CSharp 客户端”选项卡:

enter image description here

在上面的屏幕截图中可以看到第一道秘方:我们将 NodaTime 添加为附加命名空间。

更进一步,我们需要让 NSwagStudio 生成 DTO 类,并且 - 这是真正重要的事情 - 通过将它们添加到“排除的类型名称”列表来生成任何 NodaTime 类型:

enter image description here

我使用的类型排除字符串是:DateInterval,DateTimeZone,Duration,Instant,Interval,IsoDayOfWeek,LocalDate,LocalDateTime,LocalTime,Offset,OffsetDate,OffsetDateTime,OffsetTime,Period,ZonedDateTime,CalendarSystem,Era.

您还需要查看许多其他选项。完成后,按 Generate Outputs 即可生成 C# 客户端。


将客户端粘贴到引用 NodaTime 的项目中,我们可以看到使用了 NodaTime 类型:

enter image description here

我的测试使用了您的类 NodaTimeDataStructure 和这个 Controller :

[Route("api/[controller]")]
[ApiController]
public class NodaTimeController
{
    [HttpGet]
    public NodaTimeDataStructure Get() => new NodaTimeDataStructure();
}

出于此测试/演示的目的,我将其构建到一个以 .NET 4.8 为目标并使用 ASP.NET Core 2.2 的库中。

关于c# - NSwag:如何在 C# -> Swagger -> C# 客户端中使用自定义值对象类型?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/58006479/

相关文章:

c# - 如何基于numericupdown值设置计时器?

c# - 如何使用 T 参数评估其类型 c#

node.js - Sails.js API 文档

swagger - 如何使用 OpenAPI/Swagger 定义数组项的排除类型?

java - 使用 swagger codegen 生成后端代码时添加自定义注解

gradle - 使用 Gradle 配置 Swagger 代码生成器。提供的 Gradle 脚本如何工作?

c# - .NET : How to set user information in an EventLog Entry?

spring - 从 springfox 迁移到 springdoc 时出现 Null ApiResponse

swagger-codegen - 为我的 api 中的每个 Controller 生成单独的角度服务文件

c# - 对表进行反规范化或在 Entity Framework 中使用联接