Як передати параметр datetime?


86

Як передати дати UTC у веб-API?

Передача 2010-01-01працює нормально, але коли я передаю дату UTC, наприклад 2014-12-31T22:00:00.000Z(з компонентом часу), я отримую відповідь HTTP 404. Тому

http://domain/api/controller/action/2012-12-31T22:00:00.000Z

дає відповідь на помилку 404, тоді як

http://domain/api/controller/action/2012-12-31

працює нормально.

Як тоді передати дати UTC у веб-API - або принаймні вказати дату та час?


2
Чи є ":" датою підозри? Спробуйте уникнути цього. http://domain/api/controller/action/2012-12-31T22%3A00%3A00.000Z
shahkalpesh

2
Втеча не допомагає. Все ще 404.
Ніколодеон,

Чи можете ви увімкнути налагодження, щоб зрозуміти, чому переклад із переданого рядка на дату не працює? Ідея полягає в тому, щоб зрозуміти, який метод використовується для перекладу дати, яку ви передали за допомогою URL-адреси DateTime- що, як я вважаю, є типом даних параметра у вашому методі.
shahkalpesh

4
Я зроблю це. Метод очікує параметра .NET DateTime. Я думаю, це смішно, що я не можу передавати часовий компонент і не можу знайти документи, як це зробити!
Nickolodeon

2
Опублікуйте своє рішення, коли закінчите. Це може допомогти іншим людям, які мають подібні проблеми. Дякую.
shahkalpesh

Відповіді:


33

Проблема подвійна:

1. В .маршруті

За замовчуванням IIS обробляє всі URI з крапкою як статичний ресурс, намагається повернути його та взагалі пропустити подальшу обробку (за допомогою веб-API). Це налаштовано у вашому Web.config у розділі system.webServer.handlers: обробники за замовчуванням path="*.". Ви не знайдете багато документації щодо дивного синтаксису в цьому pathатрибуті (регулярний вираз мав би більше сенсу), але, мабуть, це означає "все, що не містить крапки" (і будь-який символ із пункту 2 нижче). Звідси і "безрозширення" в назві ExtensionlessUrlHandler-Integrated-4.0.

На мій погляд, у порядку "правильності" можливі декілька рішень:

  • Додайте новий обробник спеціально для маршрутів, на яких повинна бути крапка. Не забудьте додати його до типового. Для цього спершу видаліть обробник за замовчуванням і додайте його знову після свого.
  • Змініть path="*."атрибут на path="*". Тоді воно вловить усе. Зверніть увагу, що з цього моменту ваш веб-api більше не буде інтерпретувати вхідні дзвінки з крапками як статичні ресурси! Якщо ви розміщуєте статичні ресурси на своєму веб-API, це не рекомендується!
  • Додайте до свого Web.config наступне, щоб безумовно обробляти всі запити: під <system.webserver>:<modules runAllManagedModulesForAllRequests="true">

2. В :маршруті

Після того, як ви змінили вищезазначене, за замовчуванням ви отримаєте таку помилку:

Від клієнта було виявлено потенційно небезпечне значення Request.Path (:).

Ви можете змінити заздалегідь визначені заборонені / недійсні символи у своєму Web.config. Під <system.web>, додати наступне: <httpRuntime requestPathInvalidCharacters="&lt;,&gt;,%,&amp;,*,\,?" />. Я вилучив :зі стандартного списку недійсних символів.

Простіші / безпечніші рішення

Хоча це не відповідь на ваше запитання, більш безпечним і простим рішенням буде зміна запиту, щоб все це не було потрібно. Це можна зробити двома способами:

  1. Передайте дату як параметр рядка запиту, наприклад ?date=2012-12-31T22:00:00.000Z.
  2. Зняти .000з кожного запиту. Вам все одно потрібно дозволити :'s (cfr пункт 2).

Ваше "Простіше рішення" в основному зробило це за мене, оскільки мені не потрібні були секунди.
Невіл


1
У вашому "Простішому рішенні" замість того, щоб дозволяти :s, я думаю, ви можете просто використовувати %3Aзамість, :і це повинно бути нормально.
Майер Спітцер,

20

у вашому контролері веб-API продукту:

[RoutePrefix("api/product")]
public class ProductController : ApiController
{
    private readonly IProductRepository _repository;
    public ProductController(IProductRepository repository)
    {
        this._repository = repository;
    }

    [HttpGet, Route("orders")]
    public async Task<IHttpActionResult> GetProductPeriodOrders(string productCode, DateTime dateStart, DateTime dateEnd)
    {
        try
        {
            IList<Order> orders = await _repository.GetPeriodOrdersAsync(productCode, dateStart.ToUniversalTime(), dateEnd.ToUniversalTime());
            return Ok(orders);
        }
        catch(Exception ex)
        {
            return NotFound();
        }
    }
}

перевірити метод GetProductPeriodOrders у Fiddler - Композитор:

http://localhost:46017/api/product/orders?productCode=100&dateStart=2016-12-01T00:00:00&dateEnd=2016-12-31T23:59:59

Формат DateTime:

yyyy-MM-ddTHH:mm:ss

Параметр передавання javascript використовує moment.js

const dateStart = moment(startDate).format('YYYY-MM-DDTHH:mm:ss');
const dateEnd = moment(endDate).format('YYYY-MM-DDTHH:mm:ss');

18

Я відчуваю твій біль ... ще один формат часу і дати ... саме те, що тобі потрібно було!

За допомогою Web Api 2 ви можете використовувати атрибути маршруту для вказівки параметрів.

отже, з атрибутами для вашого класу та вашого методу ви можете закодувати URL-адресу REST, використовуючи цей формат utc, з яким у вас виникають проблеми (мабуть, його ISO8601, імовірно, отриманий за допомогою startDate.toISOString ())

[Route(@"daterange/{startDate:regex(^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}.\d{3}Z$)}/{endDate:regex(^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}.\d{3}Z$)}")]
    [HttpGet]
    public IEnumerable<MyRecordType> GetByDateRange(DateTime startDate, DateTime endDate)

.... АЛЕ, хоча це працює з однією датою (startDate), з якихось причин це не працює, коли endDate у такому форматі ... налагоджений годинами, лише підказка є винятком, що вона не любить двокрапку ":" (навіть хоча web.config встановлюється з:

<system.web>
    <compilation debug="true" targetFramework="4.5.1" />
    <httpRuntime targetFramework="4.5.1" requestPathInvalidCharacters="" />
</system.web>

Отже, давайте зробимо інший формат дати (взятий із полізаповнення для формату дати ISO) і додамо його до дати Javascript (для стислості перетворюємо лише на хвилини):

if (!Date.prototype.toUTCDateTimeDigits) {
    (function () {

        function pad(number) {
            if (number < 10) {
                return '0' + number;
            }
            return number;
        }

        Date.prototype.toUTCDateTimeDigits = function () {
            return this.getUTCFullYear() +
              pad(this.getUTCMonth() + 1) +
              pad(this.getUTCDate()) +
              'T' +
              pad(this.getUTCHours()) +
              pad(this.getUTCMinutes()) +
              'Z';
        };

    }());
}

Потім, коли ви надсилаєте дати методу Web API 2, ви можете перетворити їх із рядка на дату:

[RoutePrefix("api/myrecordtype")]
public class MyRecordTypeController : ApiController
{


    [Route(@"daterange/{startDateString}/{endDateString}")]
    [HttpGet]
    public IEnumerable<MyRecordType> GetByDateRange([FromUri]string startDateString, [FromUri]string endDateString)
    {
        var startDate = BuildDateTimeFromYAFormat(startDateString);
        var endDate = BuildDateTimeFromYAFormat(endDateString);
    ...
    }

    /// <summary>
    /// Convert a UTC Date String of format yyyyMMddThhmmZ into a Local Date
    /// </summary>
    /// <param name="dateString"></param>
    /// <returns></returns>
    private DateTime BuildDateTimeFromYAFormat(string dateString)
    {
        Regex r = new Regex(@"^\d{4}\d{2}\d{2}T\d{2}\d{2}Z$");
        if (!r.IsMatch(dateString))
        {
            throw new FormatException(
                string.Format("{0} is not the correct format. Should be yyyyMMddThhmmZ", dateString)); 
        }

        DateTime dt = DateTime.ParseExact(dateString, "yyyyMMddThhmmZ", CultureInfo.InvariantCulture, DateTimeStyles.AssumeUniversal);

        return dt;
    }

так що url буде

http://domain/api/myrecordtype/daterange/20140302T0003Z/20140302T1603Z

Тут Ганзельман надає деяку відповідну інформацію:

http://www.hanselman.com/blog/OnTheNightmareThatIsJSONDatesPlusJSONNETAndASPNETWebAPI.aspx


У методі WebAPI ви можете мати параметри datetime як нульовий DateTime (DateTime? StartDateString, DateTime? EndDateDtring)
DotNet Fan

Дякую за згадку про toISOString - це мене врятувало. Моя RESTful служба WCF чудово працює з двома датами в URI, тому вам не потрібні ваші складні перетворення дати. Можливо, це химерність з веб-API, який не любить двокрапки, незважаючи на налаштування конфігурації ... дивно.
Невіл

@Simon, це endDateспрацювало б, якщо б URL- адреса запиту містила косу риску вперед. На жаль, я не можу згадати, де я натрапив на цю інформацію, і не знаю, як це обійти.
Пувен,

Користувачам, що працюють цілодобово, потрібно змінити hh на HH у форматі дати.
Snaits

Це правильна відповідь. StackOverflow, ПРИПИНІТЬ ГОЛОСУВАННЯ ВІДПОВІДЕЙ!
mghaoui

8

Як аналогічну альтернативу відповіді sk, я можу передати дату, відформатовану Date.prototype.toISOString()в рядку запиту. Це стандартний формат ISO 8601, і він приймається контролерами .Net Web API без будь-якої додаткової конфігурації маршруту чи дії.

напр

var dateString = dateObject.toISOString(); // "2019-07-01T04:00:00.000Z"

1
є це? Ви могли б навести будь-який приклад, коли це працює? Я зробив те саме обхідне рішення, і це не працює.
anatol

@anatol який результат ви отримуєте? Наданий код є робочим прикладом з попередньою умовою, що dateObjectє ініціалізованим Dateоб’єктом.
Бондолін

Це, мабуть, слід трохи проголосувати. Це вирішило мою проблему, змінивши UTC на ISO. Simples
Regianni

1
@Regianni рада, що це допомогло :-)
Бондолін,

Це працювало для мене за допомогою stackoverflow.com/a/115034/1302730 для отримання дати у форматі ISO
BugLover

7

Це рішення та модель можливих рішень. Використовуйте Moment.js у своєму клієнті для форматування дат, перетворення на час unix.

 $scope.startDate.unix()

Встановіть довгі параметри маршруту.

[Route("{startDate:long?}")]
public async Task<object[]> Get(long? startDate)
{
    DateTime? sDate = new DateTime();

        if (startDate != null)
        {
            sDate = new DateTime().FromUnixTime(startDate.Value); 
        }
        else
        {
            sDate = null;
        }
         ... your code here!
  }

Створіть метод розширення для часу Unix. Метод Unix DateTime


4

Раніше це було болючим завданням, але тепер ми можемо використовувати toUTCString ():

Приклад:

[HttpPost]
public ActionResult Query(DateTime Start, DateTime End)

Помістіть нижче в запит на публікацію Ajax

data: {
    Start: new Date().toUTCString(),
    End: new Date().toUTCString()
},

3

По суті, явне вказівку параметрів як? Date = 'fulldatetime' спрацювало як привабливість. Тож це буде рішенням поки: не використовуйте коми, а використовуйте старий підхід GET.


0

Оскільки я кодую операційну систему ISO-8859-1, формат дати "dd.MM.yyyy HH: mm: sss" не був розпізнаний, тому що вдалося використати рядок InvariantCulture.

string url = "GetData?DagsPr=" + DagsProfs.ToString(CultureInfo.InvariantCulture)

0

Переглядаючи ваш код, я припускаю, що вас не турбує "час" об'єкта DateTime. Якщо так, ви можете передати дату, місяць та рік як цілі параметри. Будь ласка, перегляньте наступний код. Це робочий приклад з мого поточного проекту.

Перевага полягає в; цей метод допомагає мені уникнути проблем із форматом DateTime та несумісності культури.

    /// <summary>
    /// Get Arrivals Report Seven Day Forecast
    /// </summary>
    /// <param name="day"></param>
    /// <param name="month"></param>
    /// <param name="year"></param>
    /// <returns></returns>
    [HttpGet("arrivalreportsevendayforecast/{day:int}/{month:int}/{year:int}")]
    public async Task<ActionResult<List<ArrivalsReportSevenDayForecastModel>>> GetArrivalsReportSevenDayForecast(int day, int month, int year)
    {
        DateTime selectedDate = new DateTime(year, month, day);
        IList<ArrivalsReportSevenDayForecastModel> arrivingStudents = await _applicationService.Value.GetArrivalsReportSevenDayForecast(selectedDate);
        return Ok(arrivingStudents);
    }

Якщо ви також хочете побачити інтерфейс, сміливо читайте код нижче. На жаль, це написано Angular. Ось як я зазвичай передаю DateTime як параметр запиту в запитах Angular GET.

public getArrivalsReportSevenDayForecast(selectedDate1 : Date): Observable<ArrivalsReportSevenDayForecastModel[]> {
const params = new HttpParams();
const day = selectedDate1.getDate();
const month = selectedDate1.getMonth() + 1
const year = selectedDate1.getFullYear();

const data = this.svcHttp.get<ArrivalsReportSevenDayForecastModel[]>(this.routePrefix +
  `/arrivalreportsevendayforecast/${day}/${month}/${year}`, { params: params }).pipe(
  map<ArrivalsReportSevenDayForecastModel[], ArrivalsReportSevenDayForecastModel[]>(arrivingList => {
    // do mapping here if needed       
    return arrivingList;
  }),
  catchError((err) => this.svcError.handleError(err)));

return data;
}

0

Одним із можливих рішень є використання кліщів:

публічні довгі кліщі {get; }

Тоді в методі контролера:

public DateTime (довгі галочки);

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.