Como passar datas UTC para Web API?
Passar 2010-01-01
funciona bem, mas quando passo uma data UTC, como 2014-12-31T22:00:00.000Z
(com um componente de tempo), recebo uma resposta HTTP 404. então
http://domain/api/controller/action/2012-12-31T22:00:00.000Z
produz uma resposta de erro 404, enquanto
http://domain/api/controller/action/2012-12-31
funciona bem.
Então, como passar datas UTC para a API Web - ou pelo menos especificar data e hora?
asp.net
datetime
asp.net-web-api
Nickolodeon
fonte
fonte
http://domain/api/controller/action/2012-12-31T22%3A00%3A00.000Z
DateTime
- que presumo ser o tipo de dado do parâmetro em seu método.Respostas:
O problema é duplo:
1. O
.
na rotaPor padrão, o IIS trata todos os URIs com um ponto como recurso estático, tenta retorná-lo e ignorar o processamento adicional (pela API da Web). Isso é configurado em seu Web.config na seção
system.webServer.handlers
: os identificadores do manipulador padrãopath="*."
. Você não encontrará muita documentação sobre a sintaxe estranha nestepath
atributo (regex teria feito mais sentido), mas o que isso aparentemente significa é "qualquer coisa que não contenha um ponto" (e qualquer caractere do ponto 2 abaixo). Daí o 'Extensionless' no nomeExtensionlessUrlHandler-Integrated-4.0
.Múltiplas soluções são possíveis, na minha opinião na ordem de 'correção':
path="*."
atributo parapath="*"
. Ele então pegará tudo. Observe que, a partir de então, sua API da web não interpretará mais as chamadas recebidas com pontos como recursos estáticos! Se você estiver hospedando recursos estáticos em sua API da web, isso não é recomendado!<system.webserver>
:<modules runAllManagedModulesForAllRequests="true">
2. O
:
na rotaDepois de alterar o acima, por padrão, você obteria o seguinte erro:
Você pode alterar os caracteres não permitidos / inválidos predefinidos em seu Web.config. Sob
<system.web>
, adicione a seguinte:<httpRuntime requestPathInvalidCharacters="<,>,%,&,*,\,?" />
. Eu removi o:
da lista padrão de caracteres inválidos.Soluções mais fáceis / seguras
Embora não seja uma resposta à sua pergunta, uma solução mais segura e fácil seria alterar a solicitação para que tudo isso não seja necessário. Isso pode ser feito de duas maneiras:
?date=2012-12-31T22:00:00.000Z
..000
de cada pedido. Você ainda precisa permitir:
's (cfr ponto 2).fonte
:
s, acho que você pode apenas usar%3A
no lugar de:
e deve ficar tudo bem.em seu controlador de API da Web do produto:
[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(); } } }
teste o método GetProductPeriodOrders no Fiddler - Composer:
http://localhost:46017/api/product/orders?productCode=100&dateStart=2016-12-01T00:00:00&dateEnd=2016-12-31T23:59:59
Formato DateTime:
yyyy-MM-ddTHH:mm:ss
parâmetro de passagem javascript use moment.js
const dateStart = moment(startDate).format('YYYY-MM-DDTHH:mm:ss'); const dateEnd = moment(endDate).format('YYYY-MM-DDTHH:mm:ss');
fonte
Eu sinto sua dor ... mais um formato de data e hora ... exatamente o que você precisava!
Usando a Web Api 2, você pode usar atributos de rota para especificar parâmetros.
portanto, com atributos em sua classe e seu método, você pode codificar um URL REST usando este formato utc com o qual está tendo problemas (aparentemente, seu ISO8601, provavelmente chegou a usar 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)
.... MAS, embora isso funcione com uma data (startDate), por algum motivo não funciona quando o endDate está neste formato ... depurado por horas, apenas a dica é exceção diz que não gosta de dois pontos ":" (mesmo embora web.config seja definido com:
<system.web> <compilation debug="true" targetFramework="4.5.1" /> <httpRuntime targetFramework="4.5.1" requestPathInvalidCharacters="" /> </system.web>
Então, vamos fazer outro formato de data (retirado do polyfill para o formato de data ISO) e adicioná-lo à data Javascript (por brevidade, converta apenas em minutos):
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'; }; }()); }
Então, quando você envia as datas para o método Web API 2, pode convertê-las de string em data:
[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; }
então o url seria
http://domain/api/myrecordtype/daterange/20140302T0003Z/20140302T1603Z
Hanselman fornece algumas informações relacionadas aqui:
http://www.hanselman.com/blog/OnTheNightmareThatIsJSONDatesPlusJSONNETAndASPNETWebAPI.aspx
fonte
endDate
funcionaria se o URL da solicitação incluísse uma barra à direita. Infelizmente, não consigo me lembrar de onde encontrei essa informação, nem sei de uma maneira de contornar isso.Como alternativa semelhante à resposta de sk, posso passar uma data formatada por
Date.prototype.toISOString()
na string de consulta. Este é o formato padrão ISO 8601 e é aceito pelos controladores .Net Web API sem nenhuma configuração adicional da rota ou ação.por exemplo
var dateString = dateObject.toISOString(); // "2019-07-01T04:00:00.000Z"
fonte
dateObject
é umDate
objeto inicializado .Esta é uma solução e um modelo para possíveis soluções. Use Moment.js em seu cliente para formatar datas, converter para hora unix.
$scope.startDate.unix()
Configure seus parâmetros de rota para serem longos.
[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! }
Crie um método de extensão para o tempo Unix. Método Unix DateTime
fonte
Costumava ser uma tarefa difícil, mas agora podemos usar toUTCString ():
Exemplo:
[HttpPost] public ActionResult Query(DateTime Start, DateTime End)
Coloque o seguinte na solicitação de postagem do Ajax
data: { Start: new Date().toUTCString(), End: new Date().toUTCString() },
fonte
Na verdade, especificar parâmetros explicitamente como? Date = 'fulldatetime' funcionou perfeitamente. Portanto, esta será uma solução por enquanto: não use vírgulas, mas use a abordagem GET antiga.
fonte
Como eu tenho a codificação do sistema operacional ISO-8859-1, o formato de data "dd.MM.yyyy HH: mm: sss" não foi reconhecido, o que funcionou foi usar a string InvariantCulture.
string url = "GetData?DagsPr=" + DagsProfs.ToString(CultureInfo.InvariantCulture)
fonte
Olhando para o seu código, suponho que você não se preocupe com o 'Time' do objeto DateTime. Se sim, pode passar a data, mês e ano como parâmetros inteiros. Por favor, veja o seguinte código. Este é um exemplo prático do meu projeto atual.
A vantagem é; esse método me ajuda a evitar problemas de formato DateTime e incompatibilidades de cultura.
/// <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); }
Se você também deseja ver o front-end, sinta-se à vontade para ler o código abaixo. Infelizmente, isso está escrito em Angular. É assim que eu normalmente passo um DateTime como um parâmetro de consulta em solicitações 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; }
fonte
Uma solução possível é usar Ticks:
public long Ticks {get; }
Então, no método do controlador:
DateTime público (tiques longos);
fonte