.Net Core 3.1 Web Api 的自定义 OData 日期时间序列化程序

Posted

技术标签:

【中文标题】.Net Core 3.1 Web Api 的自定义 OData 日期时间序列化程序【英文标题】:Custom OData DateTime Serializer for .Net Core 3.1 Web Api 【发布时间】:2021-02-10 01:30:22 【问题描述】:

我有一个带有数据模型对象的 OData Api,该对象带有许多可为空的 DateTime 字段。

例如

public class Book : EntityBase

    ...
    public DateTime? CreatedDate  get; set; 
    public DateTime? UpdatedDate  get; set; 
    ...

使用 OData API 的客户端要求将 DateTime 字段格式化为“yyyy-MM-dd”格式,而不是像“yyyy-MM-ddTHH:mm:ss”这样的默认长格式

public class CustomODataSerializerProvider : DefaultODataSerializerProvider

    private readonly CustomODataEntityTypeSerializer _entityTypeSerializer;

    public CustomODataSerializerProvider(IServiceProvider rootContainer)
        : base(rootContainer)
    
        _entityTypeSerializer = new CustomODataEntityTypeSerializer(this);
    

    public override ODataEdmTypeSerializer GetEdmTypeSerializer(Microsoft.OData.Edm.IEdmTypeReference edmType)
    
        if (edmType.Definition.TypeKind == EdmTypeKind.Entity || edmType.Definition.TypeKind == EdmTypeKind.Complex)
            return _entityTypeSerializer;
        else
            return base.GetEdmTypeSerializer(edmType);
    


public class CustomODataEntityTypeSerializer : ODataResourceSerializer

    public CustomODataEntityTypeSerializer(ODataSerializerProvider provider)
        : base(provider)  

    public override Microsoft.OData.ODataProperty CreateStructuralProperty(Microsoft.OData.Edm.IEdmStructuralProperty structuralProperty, ResourceContext resourceContext)
    
        var property = base.CreateStructuralProperty(structuralProperty, resourceContext);
        if (property.Name.Contains("Date"))
        
            property.Value = ((DateTime)property.Value).ToShortDateString();
        
        return property.Value != null ? property : null;
    

我也试过“property.Value = ((DateTimeOffset)property.Value).DateTime.ToShortDateString();”而不是上面的。

然后使用注册此序列化程序

        app.UseEndpoints(endpoints =>
        
            endpoints.MapControllers();
            endpoints.EnableDependencyInjection();
            endpoints.Select().Expand().OrderBy().Filter().Count().MaxTop(10);
            endpoints.MapODataRoute("odata", "odata", a =>
            
                a.AddService(Microsoft.OData.ServiceLifetime.Singleton, typeof(IEdmModel), sp => GetEdmModel(app.ApplicationServices));
                a.AddService(Microsoft.OData.ServiceLifetime.Singleton, typeof(ODataSerializerProvider), sp => new CustomODataSerializerProvider(sp));
            );
            //endpoints.MapODataRoute("odata", "odata", GetEdmModel(app.ApplicationServices));
        );

但是当调用 OData 端点时出现此错误

 can't parse JSON.  Raw result:

"@odata.context":"https://localhost:5000/odata/$metadata#Book","value":[

我也尝试应用 Json 序列化程序,但这对从 OData 端点提供的数据没有影响:

    services
        .AddControllers()
        .AddJsonOptions(options =>
        
            options.JsonSerializerOptions.IgnoreNullValues = false;
            options.JsonSerializerOptions.Converters.Add(new DateTimeConverter());
        )
        .SetCompatibilityVersion(CompatibilityVersion.Version_3_0);

    public class DateTimeConverter : JsonConverter<DateTime>
    
        public override DateTime Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
        
            Debug.Assert(typeToConvert == typeof(DateTime));
            return DateTime.Parse(reader.GetString());
        

        public override void Write(Utf8JsonWriter writer, DateTime value, JsonSerializerOptions options)
        
            writer.WriteStringValue(value.ToUniversalTime().ToString("yyyy-MM-dd"));
        
    

我在 .NET Core 3.1 Web Api 中使用 Microsoft.AspNetCore.OData 7.4.1。任何有关如何更改通过 OData API 提供的数据的 DateTime 格式/序列化的建议将不胜感激。

【问题讨论】:

【参考方案1】:

在声明您的 EDM 时,您可以使用 Edm.Date 格式标记要反序列化的特定字段,例如:

public static IEdmModel GetEdmModel()

    ODataConventionModelBuilder builder = new ODataConventionModelBuilder();
    builder.EnableLowerCamelCase();
    builder.EntitySet<Book>("Book");
    builder.EntityType<Book>().Property(p => p.CreatedDate).AsDate();
    builder.EntityType<Book>().Property(p => p.UpdatedDate).AsDate();
    return builder.GetEdmModel();

对于一个简单的控制器,如下所示:

namespace WebAPI.Controllers

    [ApiController]
    [Route("[controller]")]
    public class BookController : ControllerBase
    
        private static readonly List<Book> Books = new List<Book>
        
            new Book() 
                BookId = 1,
                CreatedDate = new DateTime(2020, 01, 02),
                UpdatedDate = new DateTime(2020, 02, 03)
            ,
            new Book() 
                BookId = 2,
                CreatedDate = null,
                UpdatedDate = null
            ,
        ;

        [EnableQuery]
        public IEnumerable<Book> Get()
        
            var result = Books.ToArray();
            return result;
        
    

您可以看到使用 YYYY-MM-DD 格式序列化的 DateTime? 字段:


  "@odata.context": "https://localhost:5001/odata/$metadata#Book",
  "value": [
    
      "createdDate": "2020-01-02",
      "updatedDate": "2020-02-03",
      "bookId": 1
    ,
    
      "createdDate": null,
      "updatedDate": null,
      "bookId": 2
    
  ]

并且 https://localhost:5001/odata/$metadata XML 将这些字段声明为Edm.Date 类型:

<edmx:Edmx Version="4.0">
    <edmx:DataServices>
        <Schema Namespace="WebAPI">
            <EntityType Name="Book">
                <Key>
                    <PropertyRef Name="bookId"/>
                </Key>
                <Property Name="createdDate" Type="Edm.Date"/>
                <Property Name="updatedDate" Type="Edm.Date"/>
                <Property Name="bookId" Type="Edm.Int32" Nullable="false"/>
            </EntityType>
        </Schema>
        <Schema Namespace="Default">
            <EntityContainer Name="Container">
                <EntitySet Name="Book" EntityType="WebAPI.Book"/>
            </EntityContainer>
        </Schema>
    </edmx:DataServices>
</edmx:Edmx>

【讨论】:

以上是关于.Net Core 3.1 Web Api 的自定义 OData 日期时间序列化程序的主要内容,如果未能解决你的问题,请参考以下文章

如何使用 api 连接器和 asp net core web api 通过自定义声明丰富 azure b2c 令牌

NET Core 3.1 MVC 授权/身份验证,带有在单独的 Net Core 3.1 Web Api 中从外部获取的令牌 (JWT)

Wcf 服务在 .NET Core 3.1 控制台应用程序中工作,但在 ASP.NET Core 3.1 Web API 中无法工作

Asp.net core 3.1 保护 API 和 Web 应用程序

一文了解.Net Core 3.1 Web API基础知识

无法在.net core 3.1 web api中找出CORS