118 votes

Comment passer un paramètre de type datetime ?

Comment passer les dates UTC à l'API Web ?

Passing 2010-01-01 fonctionne bien, mais lorsque je passe une date UTC telle que 2014-12-31T22:00:00.000Z (avec une composante temporelle), j'obtiens une réponse HTTP 404. Donc

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

donne une réponse d'erreur 404, tandis que

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

fonctionne bien.

Comment passer les dates UTC à l'API Web - ou au moins spécifier la date ? et temps ?

49voto

Vincent Sels Points 321

Le problème est double :

1. Le site . dans l'itinéraire

Par défaut, IIS traite tous les URI contenant un point comme des ressources statiques, essaie de les renvoyer et évite tout traitement ultérieur (par l'API Web). Ceci est configuré dans votre Web.config dans la section system.webServer.handlers : le gestionnaire par défaut gère path="*." . Vous ne trouverez pas beaucoup de documentation concernant l'étrange syntaxe de ce programme. path (regex aurait été plus logique), mais ce que cela signifie apparemment est "tout ce qui ne contient pas de point" (et tout caractère du point 2 ci-dessous). D'où le "sans extension" dans le nom ExtensionlessUrlHandler-Integrated-4.0 .

Plusieurs solutions sont possibles, à mon avis dans l'ordre de la "justesse" :

  • Ajoutez un nouveau gestionnaire spécifiquement pour les routes qui doivent autoriser un point. Veillez à l'ajouter avant le défaut. Pour ce faire, assurez-vous de supprimer le gestionnaire par défaut d'abord, et le rajouter après le vôtre.
  • Changez le path="*." de l'attribut path="*" . Il capturera alors tout. Notez qu'à partir de ce moment, votre api web n'interprétera plus les appels entrants avec des points comme des ressources statiques ! Si vous hébergez des ressources statiques sur votre api web, ceci n'est donc pas conseillé !
  • Ajoutez ce qui suit à votre Web.config pour traiter inconditionnellement toutes les demandes : sous <system.webserver> : <modules runAllManagedModulesForAllRequests="true">

2. Le site : dans l'itinéraire

Après avoir modifié ce qui précède, par défaut, vous obtiendrez l'erreur suivante :

Une valeur Request.Path potentiellement dangereuse a été détectée en provenance du client ( :).

Vous pouvez modifier les caractères non autorisés/invalides prédéfinis dans votre Web.config. Sous <system.web> ajoutez ce qui suit : <httpRuntime requestPathInvalidCharacters="&lt;,&gt;,%,&amp;,*,\,?" /> . J'ai enlevé le : à partir de la liste standard des caractères non valides.

Des solutions plus simples et plus sûres

Bien que cela ne réponde pas à votre question, une solution plus sûre et plus simple serait de modifier la demande de manière à ce que tout cela ne soit pas nécessaire. Cela peut se faire de deux manières :

  1. Passez la date en tant que paramètre de chaîne de requête, comme suit ?date=2012-12-31T22:00:00.000Z .
  2. Dépouillez le .000 de chaque demande. Vous devez toujours autoriser : (cfr point 2).

35voto

FreeClimb Points 530

Dans le contrôleur de l'API Web de votre produit :

[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();
        }
    }
}

tester la méthode GetProductPeriodOrders dans Fiddler - Composer :

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

Format date/heure :

yyyy-MM-ddTHH:mm:ss

javascript passer un paramètre utiliser moment.js

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

19voto

Je ressens votre douleur ... encore un autre format de date et d'heure ... juste ce dont vous aviez besoin !

Avec Web Api 2, vous pouvez utiliser des attributs de route pour spécifier des paramètres.

Ainsi, avec les attributs de votre classe et de votre méthode, vous pouvez coder une URL REST en utilisant le format utc qui vous pose problème (apparemment, il s'agit du format ISO8601, probablement obtenu en utilisant 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)

.... MAIS, bien que cela fonctionne avec une date (startDate), pour une raison quelconque, cela ne fonctionne pas lorsque la endDate est dans ce format ... débogué pendant des heures, le seul indice est l'exception qui dit qu'elle n'aime pas les deux points " :" (même si le web.config est défini avec :

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

Créons donc un autre format de date (tiré du polyfill pour le format de date ISO) et ajoutons-le à la date Javascript (par souci de concision, convertissons uniquement jusqu'aux minutes) :

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';
        };

    }());
}

Ensuite, lorsque vous envoyez les dates à la méthode Web API 2, vous pouvez les convertir de chaîne en date :

[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;
    }

donc l'url serait

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

Hanselman donne quelques informations connexes ici :

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

13voto

NargothBond Points 362

Comme alternative similaire à la réponse de s k, je suis capable de passer une date formatée par Date.prototype.toISOString() dans la chaîne d'interrogation. Il s'agit du format standard ISO 8601, qui est accepté par les contrôleurs API Web .Net sans configuration supplémentaire de la route ou de l'action.

par exemple

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

7voto

Kentonbmax Points 614

Il s'agit d'une solution et d'un modèle de solutions possibles. Utilisez Moment.js dans votre client pour formater les dates, convertir en heure unix.

 $scope.startDate.unix()

Configurez les paramètres de votre itinéraire pour qu'il soit long.

[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!
  }

Créer une méthode d'extension pour le temps Unix. Méthode DateTime d'Unix

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X