【问题标题】:Passing UTC DateTime to Web API HttpGet Method results in local time将 UTC DateTime 传递给 Web API HttpGet 方法导致本地时间
【发布时间】:2014-04-30 03:23:32
【问题描述】:

我正在尝试将 UTC 日期作为查询字符串参数传递给 Web API 方法。网址看起来像

/api/order?endDate=2014-04-01T00:00:00Z&zoneId=4

方法的签名看起来像

[HttpGet]
public object Index(int zoneId, DateTime? endDate = null)

日期输入为31/03/2014 8:00:00 PM,但我希望输入为01/04/2014 12:00:00 AM

我的JsonFormatter.SerializerSettings 看起来像这样

new JsonSerializerSettings
{
    ContractResolver = new CamelCasePropertyNamesContractResolver(),
    DateTimeZoneHandling = DateTimeZoneHandling.Utc,
    DateFormatHandling = DateFormatHandling.IsoDateFormat
};

编辑#1: 我注意到当我发布2014-04-01T00:00:00Z 时,它将序列化为 C# 中的 UTC DateTime 类型。但是,我发现了一种解决方法,可以通过 endDate.Value.ToUniversalTime() 对其进行转换,尽管我觉得它对于 POST 而不是 GET 的工作方式很奇怪。

【问题讨论】:

  • 注意:JSON 序列化设置与 URI 绑定的参数无关。

标签: json datetime asp.net-web-api model-binding


【解决方案1】:

您发送的查询字符串参数值2014-04-01T00:00:00Z 是UTC 时间。因此,它会根据您的本地时钟转换为时间,如果您调用 ToUniversalTime(),它会转换回 UTC。

那么,问题到底是什么?如果问题是如果作为查询字符串发送但在请求正文中发布时为什么会发生这种情况,那么该问题的答案是 ASP.NET Web API 使用 model binding 和正文使用参数绑定。对于后者,它使用媒体格式化程序。如果发送 JSON,则使用 JSON 媒体格式化程序,它基于 JSON.NET。

由于您指定了DateTimeZoneHandling.Utc,它使用该设置并获得您想要的日期时间类型。顺便说一句,如果您将此设置更改为 DateTimeZoneHandling.Local,那么您将看到与模型绑定相同的行为。

【讨论】:

  • 所以我想正确的做法是保持原样并继续将查询字符串日期转换回 UTC,并解决我的问题?最好的方法是什么?
  • 这应该是答案,它非常清楚地描述了正在发生的事情。
  • 这不应该是答案。尽管它解释了正在发生的事情,但并没有为问题提供解决方案。 OP 表示他们希望时间以 UTC 的形式出现,而不是作为本地时间进入并转换为 UTC。如果这是不可能的,您的答案并没有说清楚,如果是,您没有说明如何实现。我也有同样的问题。我不想每次收到约会时都打电话给 ToUniversalTime()。我只想收到 UTC 时间。是否有一些反序列化设置会自动执行此操作?
【解决方案2】:

我最终只是使用ToUniversalTime() 方法作为参数进来。

【讨论】:

  • 我遇到了同样的问题,没有一个答案是我真正的答案,我最终得到了你的答案或添加自定义模型绑定器并将 [ModelBinder(typeof(UtcDateTimeModelBinder))] 添加到你想要的每个参数
【解决方案3】:

如果您希望转换是透明的,那么您可以使用自定义TypeConverter

public sealed class UtcDateTimeConverter : DateTimeConverter
{
    public override object ConvertFrom(ITypeDescriptorContext context, CultureInfo culture, object value)
    {
        return ((DateTime)base.ConvertFrom(context, culture, value)).ToUniversalTime();
    }
}

并使用以下方法连接:

TypeDescriptor.AddAttributes(typeof(DateTime), new TypeConverterAttribute(typeof(UtcDateTimeConverter)));

那么查询字符串参数将被实例化为DateTimeKind.Utc

【讨论】:

【解决方案4】:

因此,对于那些不想在整个应用程序中覆盖字符串到日期转换,并且不想记住修改每个采用日期参数的方法的人来说,这里是你的做法它用于 Web API 项目。

最终,一般说明来自这里:

https://docs.microsoft.com/en-us/aspnet/web-api/overview/formats-and-model-binding/parameter-binding-in-aspnet-web-api#model-binders

以下是本案例的专门说明:

  1. 在您的“WebApiConfig”类中,添加以下内容:

        var provider = new SimpleModelBinderProvider(typeof(DateTime),new UtcDateTimeModelBinder());
        config.Services.Insert(typeof(ModelBinderProvider), 0, provider);
    
  2. 创建一个名为 UtcDateTimeModelBinder 的新类:

    public class UtcDateTimeModelBinder : IModelBinder
    {
        public bool BindModel(HttpActionContext actionContext,
            ModelBindingContext bindingContext)
        {
            if (bindingContext.ModelType != typeof(DateTime)) return false;
    
            var val = bindingContext.ValueProvider.GetValue(bindingContext.ModelName);
            if (val == null)
            {
                return false;
            }
    
            var key = val.RawValue as string;
            if (key == null)
            {
                bindingContext.ModelState.AddModelError(bindingContext.ModelName,
                    "Wrong value type");
                return false;
            }
    
            DateTime result;
            if (DateTime.TryParse(key, out result))
            {
                bindingContext.Model = result.ToUniversalTime();
                return true;
            }
    
            bindingContext.ModelState.AddModelError(bindingContext.ModelName,
                "Cannot convert value to Utc DateTime");
            return false;
        }
    }
    

【讨论】:

    【解决方案5】:

    我终于找到了这段代码,它不是主要答案,但在某些情况下可以使用:

    var dateUtc = TimeZoneInfo.ConvertTimeToUtc(date);
    

    【讨论】:

      【解决方案6】:

      日期时间偏移
      我们的版本化 API 类是 automapped 到内部类。在 API 的 URL 参数模型中使用 DateTimeOffset 并添加映射 DateTimeOffset => DateTime 可以有效防止时区转换。即。

      API 类:

      public DateTimeOffset? SomeDateTime{ get; set; }
      

      内部类:

      public DateTime? SomeDateTime{ get; set; }
      

      映射配置文件:

      CreateMap<DateTimeOffset, DateTime>();
      

      【讨论】:

      • 这在 Controller 中处理 DateTime 时不起作用。见example code
      猜你喜欢
      • 2018-12-20
      • 2022-11-08
      • 1970-01-01
      • 2011-10-04
      • 1970-01-01
      • 1970-01-01
      • 2013-09-12
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多