Передача DateTimeOffset в качестве строки запроса WebAPI
у меня есть действие 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?
Я вижу, что он оставлен как null и смотрит на Request.RequestUri.Query
значение есть, просто не сопоставляется.
наконец, я попытался не делать WebUtility.UrlEncode
и ничего не изменилось.
7 ответов
проблема точно описывается ответным сообщением 400, хотя это могло быть более ясно. Маршрут, определенный атрибутом, ожидает только параметр id, но метод Delete ожидает другой параметр с именем дата.
Если вы хотите предоставить это значение с помощью строки запроса, вам нужно сделать этот параметр nullable, используя " DateTimeOffset?", что также превратит его в факультативный параметр. Если дата является обязательным полем, подумайте о ее добавлении в маршрут, например:
[Route("api/values/{id}/{date}")]
OK, игнорируйте то, что я набрал выше, это просто проблема форматирования. Web API не может определить культуру, необходимую для анализа данного значения, но если вы попытаетесь передать DateTimeOffset, используя формат JSON в строке запроса, например 2014-05-06T22:24:55Z, это должно сработать.
ответ
Отправить DateTimeOffset
ваш API, формат такой:
2017-04-17T05:04:18.070Z
полный URL API будет выглядеть следующим образом:
http://localhost:1234/api/values/1?date=2017-04-17T05:45:18.070Z
форматируя его таким образом, я могу пройти в DateTimeOffset
параметр my ApiController
что я сделал с помощью мобильных служб Azure.
код
можно использовать 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);
создайте пользовательский конвертер типов следующим образом:
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
struct:
TypeDescriptor.AddAttributes(typeof(DateTimeOffset),
new TypeConverterAttribute(typeof(DateTimeOffsetConverter)));
теперь вы можете просто передать DateTimeOffset
значения в компактной форме ISO8601, который опускает дефисы и двоеточия, которые мешают URL:
api/values/20171231T012345-0530
api/values/20171231T012345+0000
api/values/20171231T012345Z
обратите внимание, что если у вас есть доли секунды, вам может понадобиться добавлять косую черту в URL-адрес.
api/values/20171231T012345.1234567-0530/
вы также можете поместить его в строку запроса, если хотите:
api/values?foo=20171231T012345-0530
для достижения этого я использую
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);
}
}
используйте ISO 8601 формат datetime описатель:
$"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 url-адрес, содержащий параметр "date" в строке запроса. Это будет выглядеть примерно так:
http://localhost:54955/offset?дата=17.02.2015 09:25:38 +11:00
Я не смог найти перегрузку DateTimeOffset.ToString (), который будет генерировать строковое значение в этом формате, за исключением явного определения формата в строковом формате:
DateTimeOffset.Now.ToString("dd/MM/yyyy HH:mm:ss zzz")
надеюсь, что это поможет.
вот самый простой способ для тех, кто ищет какой-то синхронизации между клиентом и сервером с помощью datetime. Я реализовал это для мобильного приложения. Она независима от культуры клиента. потому что мое мобильное приложение поддерживает несколько культур и скучно использовать форматирование между этими культурами. спасибо, что .net имеет идеальные функции под названием 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);
}
}
клиент Сторона
DateTimeOffset dateTime = DateTime.Now.ToFileTime();
url= $"/PullAsync?since={datetime}&page={pageno}&count=10";