我有一个看起来像这样的WebAPI动作:
[Route("api/values/{id}")]
public async Task<HttpResponseMessage> Delete(string id, DateTimeOffset date) {
//do stuff
}
但是当我从HttpClient
实例调用它时,创建一个类似于:
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
而且没有任何不同。
答案 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