将DateTimeOffset作为WebAPI查询字符串传递
我有一个WebAPI动作,如下所示:
[Route("api/values/{id}")] public async Task 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?
我可以看到它被保留为null并查看Request.RequestUri.Query
值是否存在,只是没有映射。
最后我尝试不做WebUtility.UrlEncode
,它没有任何不同。
400响应消息正在准确地描述该问题,尽管它可能更清楚。 由属性定义的路由仅需要参数id ,但Delete方法需要另一个名为date的参数。
如果要使用查询字符串提供此值,则需要使用“DateTimeOffset?”将该参数设为可为空,这也会将其转换为可选参数。 如果日期是必填字段,请考虑将其添加到路线中,例如:
[Route("api/values/{id}/{date}")]
好的,忽略我上面输入的内容,这只是一个格式化问题。 Web API无法确定解析给定值所需的文化,但如果您尝试在查询字符串中使用JSON格式传递DateTimeOffset,例如2014-05-06T22:24:55Z,那应该可行。
回答
要将DateTimeOffset
发送到API,请将其格式化为:
2017-04-17T05:04:18.070Z
完整的API URL如下所示:
http://localhost:1234/api/values/1?date=2017-04-17T05:45:18.070Z
通过以这种方式对其进行格式化,我可以将DateTimeOffset
参数传递给我使用Azure Mobile Services ApiController
。
码
您可以使用ToString(yyy-MM-ddTHH:mm:ss.fffZ)
来解析DateTimeOffset。
var dateTimeOffsetAsAPIParameter = DateTimeOffset.UtcNow.ToString("yyy-MM-ddTHH:mm:ss.fffZ"); string.Format("http://localhost:1234/api/values/1?date={0}", dateTimeOffsetAsAPIParameter);
要实现这一点,我正在使用
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); } }
创建自定义类型转换器,如下所示:
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的紧凑forms中传递DateTimeOffset
值,这会忽略干扰URL的连字符和冒号:
api/values/20171231T012345-0530 api/values/20171231T012345+0000 api/values/20171231T012345Z
请注意,如果您有小数秒,则可能需要在url中包含尾部斜杠 。
api/values/20171231T012345.1234567-0530/
如果你愿意,你也可以把它放在查询字符串中:
api/values?foo=20171231T012345-0530
使用ISO 8601 日期时间格式说明符:
$"http://localhost:1234/api/values/1?date={DateTime.UtcNow.ToString("o")}"
要么
$"http://localhost:1234/api/values/1?date={DateTime.UtcNow:o}"
找出答案的最佳方法是要求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")
希望有所帮助。
对于那些使用datetime在客户端和服务器之间寻找某种同步的人来说,这是最简单的方法。 我为移动应用程序实现了它。 它独立于客户的文化。 因为我的移动应用支持多种文化,在这些文化之间使用格式很无聊。 谢谢.net有一个称为ToFileTime
和FromFileTime
的完美函数
Server Controller动作:
[HttpGet("PullAsync")] public async Task PullSync(long? since = null, int? page = null, int? count = null) { if (since.HasValue) DateTimeOffset date = DateTimeOffset.FromFileTime(since.Value); }
客户端
DateTimeOffset dateTime = DateTime.Now.ToFileTime(); var url= $"/PullAsync?since={datetime}&page={pageno}&count=10";