web-dev-qa-db-fra.com

Comment passer un paramètre datetime?

Comment passer les dates UTC à l'API Web?

Qui passe 2010-01-01 fonctionne bien, mais lorsque je passe une date UTC telle que 2014-12-31T22:00:00.000Z (avec une composante heure), je reçois une réponse HTTP 404. Alors

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

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

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

fonctionne bien.

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

67
Nickolodeon

Le problème est double:

1. Le . dans la route

Par défaut, IIS traite tous les URI avec un point en tant que ressource statique, essaie de le renvoyer et ignore le traitement ultérieur (par l'API Web). Ceci est configuré dans votre Web.config dans le section system.webServer.handlers: le gestionnaire par défaut gère path="*.". Vous ne trouverez pas beaucoup de documentation sur la syntaxe étrange dans cet attribut path (une expression rationnelle aurait eu plus de sens), mais cela signifie apparemment "tout ce qui ne contient pas de point" (et tout caractère de 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 "correction":

  • Ajoutez un nouveau gestionnaire spécifiquement pour les itinéraires qui doivent autoriser un point. Assurez-vous de l'ajouter avant la valeur par défaut. Pour ce faire, assurez-vous d’abord de supprimer le gestionnaire par défaut, puis de le rajouter après le vôtre.
  • Changer la path="*." attribuer à path="*". Il va alors tout attraper. 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, cela n’est donc pas conseillé!
  • Ajoutez les éléments suivants à votre Web.config pour traiter sans condition toutes les demandes: sous <system.webserver>: <modules runAllManagedModulesForAllRequests="true">

2. Le : dans la route

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

Une valeur Request.Path potentiellement dangereuse a été détectée par le client (:).

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

Des solutions plus faciles/plus sûres

Bien que ce ne soit pas une réponse à votre question, une solution plus sûre et plus simple consisterait à modifier la demande afin que tout cela ne soit pas nécessaire. Ceci peut être fait de deux façons:

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

Je ressens votre douleur ... encore un autre format de date/heure ... exactement ce dont vous aviez besoin!

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

ainsi, avec des attributs sur votre classe et votre méthode, vous pouvez coder une URL REST en utilisant ce format utc avec lequel vous rencontrez des problèmes (apparemment, son ISO8601 est probablement parvenue à l'aide de 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 (date de début), pour une raison quelconque, cela ne fonctionne pas lorsque la date de fin est dans ce format ... débogué pendant des heures, seul indice est exception dit qu'il ne aime pas les deux points ":" (même bien que web.config soit défini avec:

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

Faisons donc un autre format de date (tiré du polyfill pour le format de date ISO) et ajoutons-le à la date Javascript (par souci de brièveté, ne convertissez que les 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

16
Simon Dowdeswell

dans votre contrôleur API Web de 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();
        }
    }
}

testez 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 DateTime:

yyyy-MM-ddTHH:mm:ss

paramètre javascript pass use moment.js

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

C'est une solution et un modèle de solutions possibles. Utilisez Moment.js dans votre client pour formater les dates et les convertir en heure Unix.

 $scope.startDate.unix()

Configurez vos paramètres d'itinéraire pour qu'ils soient longs.

[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éez une méthode d'extension pour le temps Unix. Méthode Unix DateTime

6
Kentonbmax

En fait, spécifier explicitement les paramètres comme? Date = 'fulldatetime' a fonctionné comme un charme. Donc, ce sera une solution pour le moment: n'utilisez pas de virgules, mais utilisez l'ancienne approche GET.

3
Nickolodeon

C’était une tâche pénible, mais nous pouvons maintenant utiliser toUTCString ():

Exemple:

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

Mettez le ci-dessous dans Ajax post request

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

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 de requête. Il s'agit du format standard ISO 8601, accepté par les contrôleurs d'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"
0
Bondolin