Jak przekazać daty UTC do Web API?
Przekazywanie 2010-01-01
działa dobrze, ale kiedy przekażę datę UTC, taką jak 2014-12-31T22:00:00.000Z
(ze składnikiem czasu), otrzymuję odpowiedź HTTP 404. Więc
http://domain/api/controller/action/2012-12-31T22:00:00.000Z
zwraca odpowiedź o błędzie 404, podczas gdy
http://domain/api/controller/action/2012-12-31
działa w porządku.
Jak więc przekazać daty UTC do Web API - lub przynajmniej określić datę i godzinę?
asp.net
datetime
asp.net-web-api
Nickolodeon
źródło
źródło
http://domain/api/controller/action/2012-12-31T22%3A00%3A00.000Z
DateTime
- co, jak zakładam, jest typem danych parametru w Twojej metodzie.Odpowiedzi:
Problem jest dwojaki:
1. Na
.
trasieDomyślnie usługi IIS traktują wszystkie identyfikatory URI z kropką jako zasoby statyczne, próbują je zwrócić i całkowicie pominąć dalsze przetwarzanie (przez interfejs API sieci Web). Jest to skonfigurowane w pliku Web.config w sekcji
system.webServer.handlers
: domyślne uchwyty obsługipath="*."
. Nie znajdziesz zbyt wiele dokumentacji na temat dziwnej składni tegopath
atrybutu (regex miałby większy sens), ale najwyraźniej oznacza to „wszystko, co nie zawiera kropki” (i dowolny znak z punktu 2 poniżej). Stąd w nazwie słowo „Extensionless”ExtensionlessUrlHandler-Integrated-4.0
.Moim zdaniem możliwych jest wiele rozwiązań w kolejności `` poprawności '':
path="*."
atrybut napath="*"
. Wtedy złapie wszystko. Zwróć uwagę, że od tego momentu Twój internetowy interfejs API nie będzie już interpretował połączeń przychodzących z kropkami jako zasobów statycznych! Jeśli hostujesz zasoby statyczne w swoim interfejsie API sieci Web, nie jest to zalecane!<system.webserver>
:<modules runAllManagedModulesForAllRequests="true">
2. Na
:
trasiePo zmianie powyższego domyślnie pojawia się następujący błąd:
Możesz zmienić predefiniowane niedozwolone / nieprawidłowe znaki w pliku Web.config. Pod
<system.web>
dodaj następujący:<httpRuntime requestPathInvalidCharacters="<,>,%,&,*,\,?" />
. Usunąłem:
ze standardowej listy nieprawidłowych znaków.Łatwiejsze / bezpieczniejsze rozwiązania
Chociaż nie jest to odpowiedź na Twoje pytanie, bezpieczniejszym i łatwiejszym rozwiązaniem byłaby zmiana wniosku tak, aby to wszystko nie było wymagane. Można to zrobić na dwa sposoby:
?date=2012-12-31T22:00:00.000Z
..000
z każdego żądania. Nadal musiałbyś zezwolić:
(por. Punkt 2).źródło
:
s, myślę, że możesz po prostu użyć%3A
zamiast:
i powinno być dobrze.w kontrolerze Product Web 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(); } } }
przetestuj metodę GetProductPeriodOrders w programie Fiddler - Composer:
http://localhost:46017/api/product/orders?productCode=100&dateStart=2016-12-01T00:00:00&dateEnd=2016-12-31T23:59:59
Format daty i godziny:
yyyy-MM-ddTHH:mm:ss
parametr przejścia javascript użyj moment.js
const dateStart = moment(startDate).format('YYYY-MM-DDTHH:mm:ss'); const dateEnd = moment(endDate).format('YYYY-MM-DDTHH:mm:ss');
źródło
Czuję twój ból ... kolejny format daty i czasu ... dokładnie to, czego potrzebujesz!
Korzystając z interfejsu Web Api 2, możesz użyć atrybutów tras do określenia parametrów.
więc dzięki atrybutom w swojej klasie i metodzie możesz zakodować adres URL REST za pomocą tego formatu utc, z którym masz problem (najwyraźniej jego ISO8601, prawdopodobnie przy użyciu 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)
.... ALE, chociaż to działa z jedną datą (startDate), z jakiegoś powodu nie działa, gdy endDate jest w tym formacie ... debugowany przez wiele godzin, jedyną wskazówką jest wyjątek mówiący, że nie działa jak dwukropek ":" (nawet chociaż web.config jest ustawiony za pomocą:
<system.web> <compilation debug="true" targetFramework="4.5.1" /> <httpRuntime targetFramework="4.5.1" requestPathInvalidCharacters="" /> </system.web>
Stwórzmy więc inny format daty (wzięty z polyfill dla formatu daty ISO) i dodajmy go do daty JavaScript (dla zwięzłości, konwertuj tylko do minut):
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'; }; }()); }
Następnie wysyłając daty do metody Web API 2, możesz przekonwertować je z ciągu na datę:
[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; }
więc będzie to adres URL
http://domain/api/myrecordtype/daterange/20140302T0003Z/20140302T1603Z
Hanselman podaje tutaj kilka powiązanych informacji:
http://www.hanselman.com/blog/OnTheNightmareThatIsJSONDatesPlusJSONNETAndASPNETWebAPI.aspx
źródło
endDate
zadziałałoby, gdyby adres URL żądania zawierał końcowy ukośnik. Niestety nie mogę sobie przypomnieć, gdzie znalazłem te informacje, ani nie wiem, jak to obejść.Jako podobną alternatywę dla odpowiedzi sk, jestem w stanie przekazać datę sformatowaną przez
Date.prototype.toISOString()
w ciągu zapytania. Jest to standardowy format ISO 8601 i jest akceptowany przez kontrolery .Net Web API bez dodatkowej konfiguracji trasy lub akcji.na przykład
var dateString = dateObject.toISOString(); // "2019-07-01T04:00:00.000Z"
źródło
dateObject
jest zainicjowanyDate
obiekt.To jest rozwiązanie i wzór możliwych rozwiązań. Użyj Moment.js w swoim kliencie, aby sformatować daty i przekonwertować na czas unix.
$scope.startDate.unix()
Ustaw długie parametry trasy.
[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! }
Utwórz metodę rozszerzenia dla czasu uniksowego. Metoda Unix DateTime
źródło
Kiedyś było to bolesne zadanie, ale teraz możemy użyć metody toUTCString ():
Przykład:
[HttpPost] public ActionResult Query(DateTime Start, DateTime End)
Umieść poniżej w żądaniu postu Ajax
data: { Start: new Date().toUTCString(), End: new Date().toUTCString() },
źródło
W rzeczywistości jawne określanie parametrów jako? Date = 'fulldatetime' działało jak urok. Na razie to będzie rozwiązanie: nie używaj przecinków, ale użyj starego podejścia GET.
źródło
Ponieważ mam kodowanie systemu operacyjnego ISO-8859-1, format daty „dd.MM.yyyy HH: mm: sss” nie został rozpoznany, co zadziałało, to użycie ciągu InvariantCulture.
string url = "GetData?DagsPr=" + DagsProfs.ToString(CultureInfo.InvariantCulture)
źródło
Patrząc na swój kod, zakładam, że nie martwisz się o „Time” obiektu DateTime. Jeśli tak, możesz przekazać datę, miesiąc i rok jako parametry całkowite. Zobacz poniższy kod. To jest działający przykład z mojego obecnego projektu.
Zaletą jest; ta metoda pomaga mi uniknąć problemów z formatem DateTime i niezgodności kultur.
/// <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); }
Jeśli chcesz zobaczyć również front-end, przeczytaj poniższy kod. Niestety, jest to napisane w Angular. W ten sposób zwykle przekazuję DateTime jako parametr zapytania w żądaniach 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; }
źródło
Jednym z możliwych rozwiązań jest użycie Kleszczy:
public long Ticks {get; }
Następnie w metodzie kontrolera:
public DateTime (długie tiki);
źródło