Wie übergebe ich einen datetime-Parameter?

85

Wie übergebe ich UTC-Daten an die Web-API?

Das Übergeben 2010-01-01funktioniert einwandfrei, aber wenn ich ein UTC-Datum wie 2014-12-31T22:00:00.000Z(mit einer Zeitkomponente) übergebe, erhalte ich eine HTTP 404-Antwort. Damit

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

ergibt eine 404-Fehlerantwort, während

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

funktioniert gut.

Wie übergebe ich dann UTC-Daten an die Web-API - oder gebe zumindest Datum und Uhrzeit an?

Nickolodeon
quelle
2
Ist ":" im Datum ein Verdächtiger? Versuche es zu entkommen. http://domain/api/controller/action/2012-12-31T22%3A00%3A00.000Z
Shahkalpesh
2
Flucht hilft nicht. Immer noch 404.
Nickolodeon
Können Sie das Debuggen aktivieren, um herauszufinden, warum die Übersetzung von der übergebenen Zeichenfolge zum Datum fehlschlägt? Die Idee ist herauszufinden, welche Methode verwendet wird, um das Datum, an dem Sie übergeben haben, mithilfe der URL zu übersetzen. DateTimeIch gehe davon aus, dass dies der Datentyp des Parameters für Ihre Methode ist.
Shahkalpesh
4
Ich werde das machen. Die Methode erwartet den .NET DateTime-Parameter. Ich finde es lächerlich, dass ich keine Zeitkomponente weitergeben und keine Dokumente dazu finden kann!
Nickolodeon
2
Veröffentlichen Sie Ihre Lösung, wenn Sie fertig sind. Es kann anderen Menschen mit ähnlichen Problemen helfen. Vielen Dank.
Shahkalpesh

Antworten:

33

Das Problem ist zweifach:

1. Die .in der Route

Standardmäßig behandelt IIS alle URIs mit einem Punkt als statische Ressource, versucht, sie zurückzugeben und die weitere Verarbeitung (über die Web-API) insgesamt zu überspringen. Dies wird in Ihrer Web.config im Abschnitt konfiguriert system.webServer.handlers: Die Standardhandler-Handles path="*.". Sie werden in diesem pathAttribut nicht viel Dokumentation über die seltsame Syntax finden (Regex hätte mehr Sinn gemacht), aber was dies anscheinend bedeutet, ist "alles, was keinen Punkt enthält" (und jedes Zeichen aus Punkt 2 unten). Daher das 'Extensionless' im NamenExtensionlessUrlHandler-Integrated-4.0 .

Meiner Meinung nach sind mehrere Lösungen in der Reihenfolge "Korrektheit" möglich:

  • Fügen Sie einen neuen Handler speziell für die Routen hinzu, die einen Punkt zulassen müssen. Stellen Sie sicher, dass Sie es vor dem Standard hinzufügen . Stellen Sie dazu sicher, dass Sie zuerst den Standardhandler entfernen und ihn nach Ihrem wieder hinzufügen.
  • Ändern Sie das path="*."Attribut in path="*". Es wird dann alles fangen. Beachten Sie, dass Ihre Web-API von nun an eingehende Anrufe mit Punkten nicht mehr als statische Ressourcen interpretiert! Wenn Sie statische Ressourcen auf Ihrer Web-API hosten, wird dies daher nicht empfohlen!
  • Fügen Sie Ihrer Web.config Folgendes hinzu, um alle Anforderungen bedingungslos zu verarbeiten: unter <system.webserver>:<modules runAllManagedModulesForAllRequests="true">

2. Die :in der Route

Nachdem Sie das oben Gesagte geändert haben, wird standardmäßig der folgende Fehler angezeigt:

Ein möglicherweise gefährlicher Request.Path-Wert wurde vom Client erkannt (:).

Sie können die vordefinierten unzulässigen / ungültigen Zeichen in Ihrer Web.config ändern. Unter <system.web>fügen Sie die folgende: <httpRuntime requestPathInvalidCharacters="&lt;,&gt;,%,&amp;,*,\,?" />. Ich habe das :aus der Standardliste ungültiger Zeichen entfernt.

Einfachere / sicherere Lösungen

Obwohl dies keine Antwort auf Ihre Frage ist, besteht eine sicherere und einfachere Lösung darin, die Anfrage so zu ändern, dass all dies nicht erforderlich ist. Dies kann auf zwei Arten erfolgen:

  1. Übergeben Sie das Datum als Abfragezeichenfolgenparameter, z ?date=2012-12-31T22:00:00.000Z.
  2. Entfernen Sie das .000von jeder Anfrage. Sie müssten noch zulassen :(siehe Punkt 2).
Vincent Sels
quelle
Ihre "einfachere Lösung" hat es im Grunde für mich getan, da ich keine Sekunden brauchte.
Neville
Sie sind ein Lebensretter :)
Moeez
1
In Ihrer "einfacheren Lösung" können Sie, anstatt :s zuzulassen , diese %3Aanstelle von verwenden, :und es sollte in Ordnung sein.
Mayer Spitzer
18

Ich fühle deinen Schmerz ... noch ein Datum-Zeit-Format ... genau das, was du brauchst!

Mit Web Api 2 können Sie Routenattribute verwenden, um Parameter anzugeben.

Mit Attributen für Ihre Klasse und Ihre Methode können Sie eine REST-URL mit diesem utc-Format codieren, mit dem Sie Probleme haben (anscheinend mit ISO8601, vermutlich mit 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)

.... ABER, obwohl dies mit einem Datum (startDate) funktioniert, funktioniert es aus irgendeinem Grund nicht, wenn das endDate in diesem Format vorliegt ... stundenlang debuggt, nur ein Hinweis ist eine Ausnahme, die besagt, dass es keinen Doppelpunkt ":" (gerade) mag obwohl web.config gesetzt ist mit:

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

Erstellen Sie also ein anderes Datumsformat (aus der Polyfüllung für das ISO-Datumsformat entnommen) und fügen Sie es dem Javascript-Datum hinzu (der Kürze halber nur bis zu Minuten konvertieren):

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

    }());
}

Wenn Sie dann die Daten an die Web API 2-Methode senden, können Sie sie von einer Zeichenfolge in ein Datum konvertieren:

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

so wäre die url

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

Hanselman gibt hier einige verwandte Informationen:

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

Simon Dowdeswell
quelle
In der WebAPI-Methode können Sie datetime-Parameter als nullfähige DateTime (DateTime? StartDateString, DateTime? EndDateDtring) haben
DotNet Fan
Vielen Dank für die Erwähnung von toISOString - das hat mich gerettet. Mein RESTful WCF-Dienst funktioniert einwandfrei mit zwei Daten in der URI, sodass Ihre komplexen Datumskonvertierungen nicht erforderlich waren. Vielleicht ist es eine Eigenart, wenn die Web-API die Doppelpunkte trotz der Konfigurationseinstellung nicht mag ... aber seltsam.
Neville
@ Simon, das endDatewürde funktionieren, wenn die Anforderungs- URL einen abschließenden Schrägstrich enthalten würde. Leider kann ich mich nicht erinnern, wo ich auf diese Informationen gestoßen bin, und ich weiß auch nicht, wie ich das umgehen kann.
Pooven
24-Stunden-Benutzer, die dies verwenden möchten, sollten das hh im Datumsformat in HH ändern.
Snaits
Dies ist die richtige Antwort. StackOverflow, STOPPEN SIE DIE ANTWORTEN!
mghaoui
18

in Ihrem Product Web API-Controller:

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

Testen Sie die GetProductPeriodOrders-Methode in Fiddler - Composer:

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

DateTime-Format:

yyyy-MM-ddTHH:mm:ss

Javascript-Pass-Parameter verwenden moment.js

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

Als ähnliche Alternative zur Antwort von sk kann ich ein Datum übergeben, das Date.prototype.toISOString()in der Abfragezeichenfolge formatiert ist . Dies ist das Standardformat ISO 8601 und wird von .NET Web API-Controllern ohne zusätzliche Konfiguration der Route oder Aktion akzeptiert.

z.B

var dateString = dateObject.toISOString(); // "2019-07-01T04:00:00.000Z"
Bondolin
quelle
1
ist es? Können Sie ein Beispiel nennen, wo dies funktioniert? Ich habe die gleiche Problemumgehung durchgeführt und es funktioniert nicht.
Anatol
@anatol welches Ergebnis bekommst du? Der bereitgestellte Code ist ein Arbeitsbeispiel mit der Voraussetzung, dass dateObjectes sich um ein initialisiertes DateObjekt handelt.
Bondolin
Dies sollte wahrscheinlich ein bisschen abgestimmt werden. Dies löste mein Problem, indem UTC auf ISO geändert wurde. Simples
Regianni
1
@ Regianni froh, dass es geholfen hat :-)
Bondolin
Dies funktionierte bei mir mit stackoverflow.com/a/115034/1302730 , um das Datum im ISO-Format zu erhalten
BugLover
7

Dies ist eine Lösung und ein Modell für mögliche Lösungen. Verwenden Sie Moment.js in Ihrem Client, um Daten zu formatieren und in Unix-Zeit zu konvertieren.

 $scope.startDate.unix()

Richten Sie Ihre Routenparameter so ein, dass sie lang sind.

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

Erstellen Sie eine Erweiterungsmethode für die Unix-Zeit. Unix DateTime-Methode

Kentonbmax
quelle
4

Früher war es eine schmerzhafte Aufgabe, aber jetzt können wir toUTCString () verwenden:

Beispiel:

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

Fügen Sie das Folgende in die Ajax-Postanfrage ein

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

Tatsächlich funktionierte die explizite Angabe von Parametern als? Date = 'fulldatetime' wie ein Zauber. Dies ist also vorerst eine Lösung: Verwenden Sie keine Kommas, sondern den alten GET-Ansatz.

Nickolodeon
quelle
0

Da ich das Betriebssystem ISO-8859-1 codiert habe, wurde das Datumsformat "dd.MM.yyyy HH: mm: sss" nicht erkannt. Die Verwendung der InvariantCulture-Zeichenfolge funktionierte.

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

Wenn Sie sich Ihren Code ansehen, gehen Sie davon aus, dass Sie keine Bedenken hinsichtlich der 'Zeit' des DateTime-Objekts haben. In diesem Fall können Sie Datum, Monat und Jahr als ganzzahlige Parameter übergeben. Bitte beachten Sie den folgenden Code. Dies ist ein Arbeitsbeispiel aus meinem aktuellen Projekt.

Der Vorteil ist; Diese Methode hilft mir, Probleme mit dem DateTime-Format und Kulturinkompatibilitäten zu vermeiden.

    /// <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);
    }

Wenn Sie auch das Front-End sehen möchten, lesen Sie bitte den folgenden Code. Leider ist das in Angular geschrieben. So übergebe ich normalerweise eine DateTime als Abfrageparameter in Angular GET-Anforderungen.

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;
}
Kushan Randima
quelle
0

Eine mögliche Lösung ist die Verwendung von Zecken:

öffentliche lange Zecken {get; }}

Dann in der Methode des Controllers:

public DateTime (lange Häkchen);

nikolai.serdiuk
quelle