Tarih saat parametresi nasıl geçirilir?


91

UTC tarihlerini Web API'ye nasıl geçirebilirim?

Geçiş iyi 2010-01-01çalışıyor, ancak 2014-12-31T22:00:00.000Z(bir zaman bileşeni ile) gibi bir UTC tarihi geçtiğimde , bir HTTP 404 yanıtı alıyorum. Yani

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

404 hata yanıtı verirken

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

iyi çalışıyor.

UTC tarihlerini Web API'ye nasıl geçirebilirim - veya en azından tarih ve saat belirtebilirim ?


2
Tarihte ":" şüpheli mi? Kaçmayı deneyin. http://domain/api/controller/action/2012-12-31T22%3A00%3A00.000Z
shahkalpesh

2
Kaçmak yardımcı olmuyor. Hala 404.
Nickolodeon

Geçen dizeden bugüne kadar çevirinin neden başarısız olduğunu anlamak için hata ayıklamayı etkinleştirebilir misiniz? Buradaki fikir, URL'yi kullanarak geçtiğiniz tarihi çevirmek için hangi yöntemin kullanıldığını belirlemektir DateTime- ki bunun yönteminizdeki parametrenin veri türü olduğunu varsayıyorum.
shahkalpesh

4
Bunu yapacağım. Yöntem, .NET DateTime parametresini bekler. Zaman bileşenini geçirememem ve bunun nasıl yapılacağına dair dokümanlar bulamamam çok saçma!
Nickolodeon

2
Çözümünüzü tamamladığınızda gönderin. Benzer problemi olan diğer insanlara yardımcı olabilir. Teşekkürler.
shahkalpesh

Yanıtlar:


34

Sorun iki yönlüdür:

1. The . Rotadaki

Varsayılan olarak IIS, içinde nokta bulunan tüm URI'leri statik kaynak olarak değerlendirir, geri döndürmeye çalışır ve daha fazla işlemeyi (Web API ile) tamamen atlamaya çalışır. Bu, Web.config dosyanızda şu bölümde yapılandırılır system.webServer.handlers: varsayılan işleyici tutamaçları path="*.". Bu pathöznitelikte garip sözdizimi ile ilgili çok fazla belge bulamazsınız (normal ifade daha mantıklı olurdu), ancak bunun anlamı "nokta içermeyen herhangi bir şey" (ve aşağıdaki 2. maddeden herhangi bir karakter). Dolayısıyla adındaki 'Uzantısız' ExtensionlessUrlHandler-Integrated-4.0.

Bence 'doğruluk' sırasına göre birden fazla çözüm mümkündür:

  • Bir noktaya izin vermesi gereken yollar için özel olarak yeni bir işleyici ekleyin. Varsayılandan önce eklediğinizden emin olun . Bunu yapmak için, lütfen yapmak kaldırmak önce varsayılan işleyiciyi ve sonra tekrar eklediğinizden .
  • Değişim path="*."için özniteliği path="*". Daha sonra her şeyi yakalayacaktır. O andan itibaren, web api'nizin artık noktalı gelen aramaları statik kaynaklar olarak yorumlamayacağını unutmayın! Statik kaynakları web api'nizde barındırıyorsanız, bu nedenle tavsiye edilmez!
  • Tüm istekleri koşulsuz olarak işlemek için Web.config dosyanıza aşağıdakileri ekleyin: altında <system.webserver>:<modules runAllManagedModulesForAllRequests="true">

2. :Rotadaki

Yukarıdakini değiştirdikten sonra, varsayılan olarak aşağıdaki hatayı alırsınız:

İstemciden (:) potansiyel olarak tehlikeli bir Request.Path değeri algılandı.

Web.config dosyanızda önceden tanımlanmış izin verilmeyen / geçersiz karakterleri değiştirebilirsiniz. Altında <system.web>aşağıdakileri ekleyin: <httpRuntime requestPathInvalidCharacters="&lt;,&gt;,%,&amp;,*,\,?" />. Kaldırdım:Standart geçersiz karakterler listesinden .

Daha kolay / daha güvenli çözümler

Sorunuzun cevabı olmasa da, daha güvenli ve daha kolay bir çözüm, tüm bunların gerekli olmaması için talebi değiştirmek olacaktır. Bu iki şekilde yapılabilir:

  1. Tarihi, gibi bir sorgu dizesi parametresi olarak iletin ?date=2012-12-31T22:00:00.000Z.
  2. Soyun .000her istekten. Yine de :'s'ye izin vermeniz gerekir (cfr nokta 2).

Saniyeye ihtiyacım olmadığından "Daha Kolay Çözüm" temelde benim için yaptı.
Neville

Sen bir hayat kurtarıcısın :)
Moeez

1
Senin "Kolay Çözüm" olarak, yerine izin verme :s, ben sadece kullanabilirsiniz düşünmek %3Ayerine :ve ince olmalıdır.
Mayer Spitzer

22

Ürün Web API denetleyicinizde:

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

Fiddler - Composer'da GetProductPeriodOrders yöntemini test edin:

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

DateTime biçimi:

yyyy-MM-ddTHH:mm:ss

javascript geçiş parametresi moment.js kullanın

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

18

Acını hissediyorum ... yine başka bir tarih ve saat biçimi ... tam da ihtiyacın olan şey!

Web Api 2'yi kullanarak, parametreleri belirtmek için rota niteliklerini kullanabilirsiniz.

Bu nedenle, sınıfınız ve yönteminizdeki özniteliklerle sorun yaşadığınız bu utc biçimini kullanarak bir REST URL'si kodlayabilirsiniz (görünüşe göre ISO8601, muhtemelen startDate.toISOString () kullanmaya geldi)

[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)

.... AMA, bu bir tarihle (başlangıçTarihi) çalışmasına rağmen, bazı nedenlerden dolayı, bitişTarihi bu formatta olduğunda çalışmaz ... saatlerce hata ayıklanır, yalnızca ipucu istisnadır, kolondan hoşlanmadığını söyler ":" (hatta web.config şu şekilde ayarlanmış olsa da:

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

Öyleyse, başka bir tarih formatı yapalım (ISO tarih formatı için çoklu dolgudan alınmıştır) ve onu Javascript tarihine ekleyelim (kısalık için, yalnızca dakikalara kadar dönüştürün):

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

    }());
}

Ardından tarihleri ​​Web API 2 yöntemine gönderdiğinizde, bunları dizeden bugüne dönüştürebilirsiniz:

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

yani url

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

Hanselman burada bazı ilgili bilgiler veriyor:

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


WebAPI yönteminde, datetime parametrelerini null yapılabilir DateTime (DateTime? StartDateString, DateTime? EndDateDtring)
DotNet Fan

ToISOString'den bahsettiğiniz için teşekkürler - bu beni kurtardı. RESTful WCF Hizmetim URI'de iki tarihle sorunsuz çalışıyor, bu nedenle karmaşık tarih dönüşümlerinize ihtiyaç duymadı. Belki Web API'sinin yapılandırma ayarına rağmen iki nokta üst üste işaretlerini sevmemesi bir tuhaftır ... yine de garip.
Neville

@Simon, endDateistek URL'si sonda bir eğik çizgi içeriyorsa işe yarar. Ne yazık ki bu bilgiye nerede rastladığımı hatırlayamıyorum ve bununla ilgili bir yol bilmiyorum.
Pooven

Bunu kullanmak isteyen 24 saatlik saat kullanıcıları, hh'yi tarih formatında HH olarak değiştirmelidir.
2017

1
Bu doğru cevap. StackOverflow, CEVAPLARI AZALTMAYI DURDURUN!
mghaoui

9

Sk'nin cevabına benzer bir alternatif olarak, şu şekilde biçimlendirilmiş bir tarihi geçebiliyorum: Date.prototype.toISOString() , sorgu dizesinde . Bu, standart ISO 8601 formatıdır ve .Net Web API denetleyicileri tarafından herhangi bir ek yol veya eylem yapılandırması olmaksızın kabul edilir.

Örneğin

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

1
bu mu? bunun işe yaradığı herhangi bir örnek verebilir misiniz? Ben de aynı çözümü yaptım ve işe yaramıyor.
anatol

@anatol ne sonucu elde edersiniz? Sağlanan kod, dateObjectbaşlatılmış bir Datenesne olan ön koşul ile çalışan bir örnektir .
Bondolin

Bu muhtemelen biraz oylanmalı. Bu, UTC'yi ISO olarak değiştirerek sorunumu çözdü. Simples
Regianni

1
@Regianni yardımcı olduğuna sevindim :-)
Bondolin

Bu , ISO biçiminde tarihi almak için stackoverflow.com/a/115034/1302730 kullanarak benim için çalıştı
BugLover

7

Bu bir çözüm ve olası çözümler için bir modeldir. Tarihleri ​​biçimlendirmek, unix zamanına dönüştürmek için istemcinizde Moment.js kullanın.

 $scope.startDate.unix()

Rota parametrelerinizi uzun olacak şekilde ayarlayın.

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

Unix zamanı için bir uzatma yöntemi oluşturun. Unix DateTime Yöntemi


4

Eskiden zahmetli bir görevdi, ancak artık toUTCString () kullanabiliriz:

Misal:

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

Aşağıdakileri Ajax gönderi isteğine ekleyin

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

3

Nitekim, parametrelerin açıkça? Date = 'fulldatetime' olarak belirtilmesi bir cazibe gibi çalıştı. Bu yüzden şimdilik bu bir çözüm olacak: virgül kullanmayın, eski GET yaklaşımını kullanın.


0

ISO-8859-1 işletim sistemini kodladığım için, "gg.MM.yyyy HH: mm: sss" tarih formatı tanınmadı, işe yarayan şey InvariantCulture dizesini kullanmaktı.

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

0

Kodunuza bakarak, DateTime nesnesinin 'Saati' ile ilgili bir endişenizin olmadığını varsayıyorum. Eğer öyleyse, tarihi, ayı ve yılı tamsayı parametreleri olarak geçirebilirsiniz. Lütfen aşağıdaki koda bakın. Bu, mevcut projemden çalışan bir örnek.

Avantajı; bu yöntem, DateTime biçimi sorunlarını ve kültür uyumsuzluklarını önlememe yardımcı oluyor.

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

Ön ucu da görmek istiyorsanız, aşağıdaki kodu okumaktan çekinmeyin. Ne yazık ki bu Angular ile yazılmıştır. Angular GET isteklerinde normalde bir DateTime'ı sorgu parametresi olarak bu şekilde geçiriyorum.

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

0

Olası bir çözüm, Keneler kullanmaktır:

public long Ticks {get; }

Ardından denetleyicinin yönteminde:

halka açık DateTime (uzun tıklamalar);

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.