将DateTimeOffset作为Web API查询字符串传递

时间:2015-02-16 20:25:56

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

我有一个看起来像这样的WebAPI动作:

[Route("api/values/{id}")]
public async Task<HttpResponseMessage> Delete(string id, DateTimeOffset date) {
    //do stuff
}

但是当我从HttpClient实例调用它时,创建一个类似于:

的URL
string.Format("http://localhost:1234/api/values/1?date={0}", System.Net.WebUtility.UrlEncode(DateTimeOffset.Now.ToString()));
// -> "http://localhost:1234/api/values/1?date=17%2F02%2F2015+7%3A18%3A39+AM+%2B11%3A00"

我收到400回复,说不可存在的参数date不存在。

我也尝试将[FromUri]属性添加到参数中,但它仍然没有映射。如果我将其更改为DateTimeOffset?,我可以看到它保留为空并查看Request.RequestUri.Query值,那里没有映射。

最后我尝试不做WebUtility.UrlEncode而且没有任何不同。

9 个答案:

答案 0 :(得分:12)

答案

要向您的API发送DateTimeOffset,请在将其转换为UTC后将其格式化为:

2017-04-17T05:04:18.070Z

完整的API网址如下所示:

http://localhost:1234/api/values/1?date=2017-04-17T05:45:18.070Z

首先将DateTimeOffset转换为UTC非常重要,因为,当@OffHeGoes指出in the comments时,字符串末尾的Z表示Zulu Time(更常见)称为UTC)。

代码

您可以使用.ToUniversalTime().ToString(yyyy-MM-ddTHH:mm:ss.fffZ)来解析DateTimeOffset。

为确保使用正确的时区格式化DateTimeOffset,请始终使用.ToUniversalTime()DateTimeOffset值转换为UTC,因为字符串末尾的Z表示UTC,aka &#34; Zulu Time&#34;。

DateTimeOffset currentTime = DateTimeOffset.UtcNow;
string dateTimeOffsetAsAPIParameter = currentDateTimeOffset.ToUniversalTime().ToString("yyyy-MM-ddTHH:mm:ss.fffZ");
string apiUrl = string.Format("http://localhost:1234/api/values/1?date={0}", dateTimeOffsetAsAPIParameter);

答案 1 :(得分:3)

问题正在由400响应消息准确描述,尽管它可能更清楚。由属性定义的路径仅需要参数 id ,但Delete方法需要另一个名为 date 的参数。

如果要使用查询字符串提供此值,则需要使用“DateTimeOffset?”将该参数设为可为空,这也会将其转换为可选参数。如果日期是必填字段,请考虑将其添加到路线中,例如:

[Route("api/values/{id}/{date}")]

<击>

好的,忽略我上面输入的内容,这只是一个格式化问题。 Web API无法确定解析给定值所需的文化,但是如果您尝试在查询字符串中使用JSON格式传递DateTimeOffset,例如2014-05-06T22:24:55Z,则应该可以正常工作。

答案 2 :(得分:2)

为实现这一目标,我正在使用

internal static class DateTimeOffsetExtensions
{
    private const string Iso8601UtcDateTimeFormat = "yyyy-MM-ddTHH:mm:ssZ";

    public static string ToIso8601DateTimeOffset(this DateTimeOffset dateTimeOffset)
    {
        return dateTimeOffset.ToUniversalTime().ToString(Iso8601UtcDateTimeFormat);
    }
}

答案 3 :(得分:2)

按如下方式创建自定义类型转换器:

public class DateTimeOffsetConverter : TypeConverter
{
    public override bool CanConvertFrom(ITypeDescriptorContext context, Type sourceType)
    {
        if (sourceType == typeof(string))
            return true;

        return base.CanConvertFrom(context, sourceType);
    }

    public override bool CanConvertTo(ITypeDescriptorContext context, Type destinationType)
    {
        if (destinationType == typeof(DateTimeOffset))
            return true;

        return base.CanConvertTo(context, destinationType);
    }

    public override object ConvertFrom(ITypeDescriptorContext context, CultureInfo culture, object value)
    {
        var s = value as string;

        if (s != null)
        {
            if (s.EndsWith("Z", StringComparison.OrdinalIgnoreCase))
            {
                s = s.Substring(0, s.Length - 1) + "+0000";
            }

            DateTimeOffset result;
            if (DateTimeOffset.TryParseExact(s, "yyyyMMdd'T'HHmmss.FFFFFFFzzz", CultureInfo.InvariantCulture, DateTimeStyles.None, out result))
            {
                return result;
            }
        }

        return base.ConvertFrom(context, culture, value);
    }

在启动序列中,例如WebApiConfig.Register,将此类型转换器动态添加到DateTimeOffset结构:

TypeDescriptor.AddAttributes(typeof(DateTimeOffset),
                             new TypeConverterAttribute(typeof(DateTimeOffsetConverter)));

您现在可以在ISO8601的紧凑形式中传递DateTimeOffset值,这会忽略干扰URL的连字符和冒号:

api/values/20171231T012345-0530
api/values/20171231T012345+0000
api/values/20171231T012345Z

请注意,如果您有小数秒,则可能需要include a trailing slash in the url

api/values/20171231T012345.1234567-0530/

如果您愿意,也可以将它放在查询字符串中:

api/values?foo=20171231T012345-0530

答案 4 :(得分:2)

使用ISO 8601 datetime format说明符:

$"http://localhost:1234/api/values/1?date={DateTime.UtcNow.ToString("o")}"

$"http://localhost:1234/api/values/1?date={DateTime.UtcNow:o}"

答案 5 :(得分:1)

当前接受的答案会丢弃时区信息,这在某些情况下很重要。以下内容将维持时区,并且不会丢失任何精度。构建查询字符串时,还可以使您的代码简洁。

public static string UrlEncode(this DateTimeOffset dateTimeOffset)
{
     return HttpUtility.UrlEncode(dateTimeOffset.ToString("o"));
}

答案 6 :(得分:0)

最好的方法是让WebAPI自己生成预期的URL格式:

public class OffsetController : ApiController
{
    [Route("offset", Name = "Offset")]
    public IHttpActionResult Get(System.DateTimeOffset date)
    {
        return this.Ok("Received: " + date);
    }

    [Route("offset", Name = "Default")]
    public IHttpActionResult Get()
    {
        var routeValues = new { date = System.DateTimeOffset.Now };
        return this.RedirectToRoute("Offset", routeValues);
    }
}

当调用/ offset时,响应将返回302到包含查询字符串中'date'参数的url。 它看起来像这样:

  

http://localhost:54955/offset?date=02/17/2015 09:25:38 +11:00

我找不到DateTimeOffset.ToString()的重载,它会生成该格式的字符串值,除非以字符串格式显式定义格式:

DateTimeOffset.Now.ToString("dd/MM/yyyy HH:mm:ss zzz")

希望有所帮助。

答案 7 :(得分:0)

对于那些使用datetime在客户端和服务器之间寻求某种同步的人来说,这是最简单的方法。我实现了移动应用程序。它独立于客户的文化。因为我的移动应用支持多种文化,并且在这些文化之间使用格式很无聊。感谢.net具有完善的功能,称为ax = sns.barplot("size", y="total_bill", data=tips, palette="Blues_d") ToFileTime

服务器控制器操作:

FromFileTime

客户端

[HttpGet("PullAsync")]
public async Task<IActionResult> PullSync(long? since = null, int? page = null, int? count = null)
{
    if (since.HasValue) 
        DateTimeOffset date = DateTimeOffset.FromFileTime(since.Value);    
}

答案 8 :(得分:0)

问题在于偏移部分中的 +(加号) 字符,我们应该对其进行编码。

如果偏移量是-(减号)不需要编码

+(plus) 的编码值为 %2B 所以 2021-05-05T18:00:00+05:00 应该作为 2021-05-05T18:00:00%2B05:00

传递
http://localhost:1234/api/values/1?date=2021-05-05T18:00:00%2B05:00

如果偏移量是 -(减号) 那么

http://localhost:1234/api/values/1?date=2021-05-05T18:00:00-05:00