【问题标题】:Controlling DateTime parameter formatting in WebAPI 2在 WebAPI 2 中控制 DateTime 参数格式
【发布时间】:2016-09-21 23:37:27
【问题描述】:

所以我有一个用 C# 编写的 WebAPI 2 控制器,其中包含一个 DateTime 类型的查询参数。这是一个基于日期过滤器从数据存储中返回所有值的 API。 比如说:

public MyThing GetThing([FromUri]DateTime startTime)
{
 // filter and return some results
}

我遇到了两个问题:

  1. 出于某种原因,尽管传入了 ISO 8601 UTC 格式(带有 Z)日期,但 WebAPI 将其反序列化为本地 DateTime,而不是 Utc。这显然是不可取的。我不确定如何修改管道以使其正确理解 UTC-0 DateTimes。
  2. 我将一个链接返回到作为响应正文的一部分的资源,其中我使用 UrlHelper 对象(从父 ApiController 抽象类获得)Link() 方法来生成一个 href。我正在传递一组我想添加到路由中的查询参数。无论出于何种原因,传递 DateTime 都会将其格式化为非 ISO8601 格式。我找不到这是在哪里控制的。我不想明确 ToString() 它,因为这不能普遍执行。

简而言之,我想弄清楚如何确保这一点

  1. 通过 FromUri 查询参数传入的日期时间被正确理解为 ISO8601,包括适当的时区偏移
  2. UrlHelper.Link() 以通用的静态类型方式在输出 URI 字符串中生成符合 ISO8601 的日期时间。

WebAPI 2 确实提供了用于格式化 JSON 的精彩钩子,我确实使用了它,因此只需返回 JSON 正文中的 DateTime 即可使用 ISO8601 格式根据需要对其进行格式化,并且在 [FromBody] 中也可以正确理解JSON 正文。虽然我找不到围绕 URI 处理拉字符串的方法,但我真的很想这样做!

【问题讨论】:

  • 关于 UrlHelper,它似乎有点过时了,不确定。我相信它以(内部)Bind 方法结束:sourcebrowser.io/Browse/ASP-NET-MVC/aspnetwebstack/src/… 如您所见,它只是在参数上执行Convert.ToString(value, CultureInfo.InvariantCulture)。因此,唯一的解决方案是添加一个覆盖 ToString() 的 DateTime 包装类以发送回您想要的内容。
  • 对于FromUri,我建议你使用[ValueProvider(typeof(MyUriValueProviderFactory))]而不是[FromUri]并实现MyUriValueProviderFactory。它必须派生自 ValueProviderFactory。
  • 这可能会有所帮助:stackoverflow.com/questions/22581138/…
  • 约会总是一个巨大的痛苦。 10 次中有 9 次更容易将其作为字符串传递,然后在您自己的代码中使用您需要的任何转换。
  • 你在使用路由属性吗?过去我遇到了日期问题,但例如对于方法public async Task<IHttpActionResult> GetDetails(DateTime? start = null, DateTime? end = null),我正在使用这条路线[Route("details/{start:datetime:regex(\\d{4}-\\d{2}-\\d{2})?}/{end:datetime:regex(\\d{4}-\\d{2}-\\d{2})?}")]

标签: c# datetime asp.net-web-api format


【解决方案1】:

您可以使用 modelbinder 将传入数据转换为您的模型。

GetThings([ModelBinder(typeof(UtcDateTimeModelBinder)), FromUri] DateTime dt){//do somthing}


public class UtcDateTimeModelBinder : IModelBinder
{
    public object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext)
    {

        if (bindingContext == null)
        {
            throw new ArgumentNullException(nameof(bindingContext));
        }

        if (bindingContext.ModelMetadata.ModelType == typeof(DateTime))
        {
            var valueProviderResult = bindingContext.ValueProvider.GetValue(bindingContext.ModelName);
            var str = valueProviderResult.AttemptedValue;
            return DateTime.Parse(str).ToUniversalTime();
        }

        return null;
    }

这样你就可以将它设置为日期时间的默认模型。

ModelBinders.Binders.Add(typeof(DateTime), new UtcDateTimeModelBinder());

【讨论】:

  • 这并不能解决链接问题,但模型活页夹是日期问题的最佳解决方案
  • 是的,没错。链接问题已在其他帖子中解决。
【解决方案2】:

如果您想保持 UTC 偏移量,为什么不使用 DateTimeOffset 而不是 DateTime? 以下是一些使用 JSON 序列化的代码:

API 控制器:

public class ValuesController : ApiController
{
    public object Get(DateTimeOffset dt)
    {
        return new {
            Input = dt,
            Local = dt.LocalDateTime,
            Utc = dt.UtcDateTime
        };
    }
}

Razor View 示例代码(假设您在 MVC + Web API Visual Studio 模板中创建了默认 api 路由)

<a href="@Url.RouteUrl("DefaultApi",new {httproute = "",controller = "values",dt = DateTimeOffset.UtcNow})">Utc Now</a>

渲染为:

<a href="/api/values?dt=06%2F26%2F2018%2009%3A37%3A24%20%2B00%3A00">Utc Now</a>

您可以使用日期时间偏移量调用您的 API:

2018-06-26T08:25:48Z: http://localhost:1353/api/values?dt=2018-06-26T08:25:48Z
{"Input":"2018-06-26T08:25:48+00:00","Local":"2018-06-26T10:25:48+02:00","Utc":"2018-06-26T08:25:48Z"}

2018-06-26T08:25:48+01:00: http://localhost:1353/api/values?dt=2018-06-26T08%3A25%3A48%2B01%3A00 (note that : and + must be url encoded)
{"Input":"2018-06-26T08:25:48+01:00","Local":"2018-06-26T09:25:48+02:00","Utc":"2018-06-26T07:25:48Z"}

【讨论】:

    【解决方案3】:

    您发送的查询字符串参数值是UTC时间。所以, 同样会根据您的本地时钟转换为时间,如果 你打电话给ToUniversalTime(),它会被转换回UTC。

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

    由于您指定了 DateTimeZoneHandling.Utc,它使用它 设置并获得所需的日期时间类型。顺便说一句,如果你改变 将此设置为 DateTimeZoneHandling.Local,然后您将看到相同的 行为作为模型绑定。

    意思是为了得到你想要的格式,你需要做的就是调用ToUniversalTime()方法。

    【讨论】:

    • 操作者希望它是模型绑定的,甚至为此提供了赏金,可能有其他原因应该这样做,例如这是在现有项目中并且用户不想要到在每个地方手动调用它。请重构你的答案
    【解决方案4】:

    1.

    您应该检查参数“startTime”的时区(应该是您的服务器/计算机的时区)。

    Web API 提供的 DateTime 是正确的,它只是取决于您的时区。

    2.

    创建一个 Json DateTime 序列化程序以生成 ISO8601 格式的日期。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2020-02-28
      • 2018-12-24
      • 1970-01-01
      • 2019-06-08
      • 2015-01-11
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多