จะส่งผ่านพารามิเตอร์ datetime ได้อย่างไร


91

จะส่งวันที่ UTC ไปยัง Web API ได้อย่างไร

การผ่านใช้2010-01-01งานได้ดี แต่เมื่อฉันผ่านวันที่ UTC เช่น2014-12-31T22:00:00.000Z(พร้อมส่วนประกอบเวลา) ฉันได้รับการตอบสนอง HTTP 404 ดังนั้น

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

ให้การตอบสนองข้อผิดพลาด 404 ในขณะที่

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

ใช้งานได้ดี

จะส่งวันที่ UTC ไปยัง Web API ได้อย่างไร - หรืออย่างน้อยระบุวันที่และเวลา?


2
":" ในวันที่ผู้ต้องสงสัยหรือไม่? ลองหลีกหนีมัน http://domain/api/controller/action/2012-12-31T22%3A00%3A00.000Z
shahkalpesh

2
การหนีไม่ได้ช่วยอะไร ยังคงเป็น 404
ตู้เพลง

คุณสามารถเปิดใช้งานการดีบักเพื่อดูว่าเหตุใดการแปลจากสตริงที่ส่งถึงวันที่ล้มเหลว แนวคิดคือการคิดว่าจะใช้วิธีใดในการแปลวันที่ที่คุณส่งผ่านโดยใช้ URL ไปDateTimeซึ่งฉันคิดว่าเป็นประเภทข้อมูลของพารามิเตอร์ในวิธีการของคุณ
shahkalpesh

4
ฉันจะทำมัน. เมธอดต้องการพารามิเตอร์. NET DateTime ฉันคิดว่ามันไร้สาระที่ฉันไม่สามารถผ่านองค์ประกอบของเวลาและไม่พบเอกสารเกี่ยวกับวิธีการทำเช่นนั้น!
ตู้เพลง

2
โพสต์วิธีแก้ปัญหาของคุณเมื่อคุณทำเสร็จแล้ว สามารถช่วยคนอื่น ๆ ที่มีปัญหาคล้ายกัน ขอบคุณ.
shahkalpesh

คำตอบ:


34

ปัญหามีสองเท่า:

1. .อยู่ในเส้นทาง

ตามค่าเริ่มต้น IIS จะถือว่า URI ทั้งหมดมีจุดอยู่ในนั้นเป็นทรัพยากรแบบคงที่พยายามส่งคืนและข้ามการประมวลผลเพิ่มเติม (โดย Web API) ทั้งหมด นี้มีการกำหนดค่าใน Web.config ของคุณในส่วนsystem.webServer.handlers: path="*."จับดำเนินการเริ่มต้น คุณจะไม่พบเอกสารเกี่ยวกับไวยากรณ์แปลก ๆ ในpathแอตทริบิวต์นี้มากนัก(regex น่าจะเข้าท่ากว่า) แต่สิ่งที่เห็นได้ชัดคือ "อะไรก็ได้ที่ไม่มีจุด" (และอักขระใด ๆ จากจุดที่ 2 ด้านล่าง) เพราะฉะนั้น 'extensionless' ExtensionlessUrlHandler-Integrated-4.0ในชื่อ

เป็นไปได้หลายวิธีในความคิดของฉันตามลำดับ 'ความถูกต้อง':

  • เพิ่มตัวจัดการใหม่เฉพาะสำหรับเส้นทางที่ต้องอนุญาตจุด อย่าลืมเพิ่มก่อนค่าเริ่มต้น ในการดำเนินการนี้ตรวจสอบให้แน่ใจว่าคุณได้ลบตัวจัดการเริ่มต้นออกก่อนและเพิ่มกลับหลังจากของคุณ
  • เปลี่ยนpath="*."แอตทริบิวต์เป็นpath="*". จากนั้นจะจับทุกอย่าง โปรดทราบว่าจากนั้น API เว็บของคุณจะไม่ตีความสายเรียกเข้าที่มีจุดเป็นทรัพยากรแบบคงที่อีกต่อไป! หากคุณกำลังโฮสต์ทรัพยากรแบบคงที่บนเว็บ API ของคุณไม่แนะนำให้ทำเช่นนั้น!
  • เพิ่มสิ่งต่อไปนี้ใน Web.config ของคุณเพื่อจัดการคำขอทั้งหมดโดยไม่มีเงื่อนไข: ภายใต้<system.webserver>:<modules runAllManagedModulesForAllRequests="true">

2. :อยู่ในเส้นทาง

หลังจากที่คุณเปลี่ยนแปลงข้างต้นตามค่าเริ่มต้นคุณจะได้รับข้อผิดพลาดต่อไปนี้:

ตรวจพบ Request.Path ที่อาจเป็นอันตรายจากไคลเอนต์ (:)

คุณสามารถเปลี่ยนอักขระที่ไม่ได้รับอนุญาต / ไม่ถูกต้องที่กำหนดไว้ล่วงหน้าใน Web.config ของคุณ ภายใต้<system.web>เพิ่มสิ่งต่อไปนี้: <httpRuntime requestPathInvalidCharacters="&lt;,&gt;,%,&amp;,*,\,?" />. ฉันได้ลบ:อักขระที่ไม่ถูกต้องออกจากรายการมาตรฐานแล้ว

โซลูชันที่ง่ายขึ้น / ปลอดภัยยิ่งขึ้น

แม้ว่าจะไม่ใช่คำตอบสำหรับคำถามของคุณ แต่วิธีแก้ปัญหาที่ปลอดภัยและง่ายกว่าคือการเปลี่ยนคำขอเพื่อให้ไม่จำเป็นต้องทำทั้งหมดนี้ สามารถทำได้สองวิธี:

  1. ส่งวันที่เป็นพารามิเตอร์สตริงการสืบค้นเช่น?date=2012-12-31T22:00:00.000Z.
  2. คัดลอก.000จากทุกคำขอ คุณยังต้องอนุญาต:(cfr point 2)

"วิธีแก้ปัญหาที่ง่ายกว่า" ของคุณทำเพื่อฉันโดยที่ฉันไม่ต้องใช้เวลาสักวินาที
Neville

คุณคือผู้ช่วยชีวิต :)
Moeez

1
ใน "วิธีแก้ปัญหาที่ง่ายกว่า" ของคุณแทนที่จะอนุญาต:ฉันคิดว่าคุณสามารถใช้%3Aแทนได้:และน่าจะใช้ได้
Mayer Spitzer

22

ในตัวควบคุม Product Web API ของคุณ:

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

ทดสอบเมธอด GetProductPeriodOrders ใน Fiddler - Composer:

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

รูปแบบ DateTime:

yyyy-MM-ddTHH:mm:ss

พารามิเตอร์ javascript pass ใช้ moment.js

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

18

ฉันรู้สึกถึงความเจ็บปวดของคุณ ... ยังมีรูปแบบวันเวลาอื่น ... สิ่งที่คุณต้องการ!

การใช้ Web Api 2 คุณสามารถใช้คุณลักษณะเส้นทางเพื่อระบุพารามิเตอร์

ดังนั้นด้วยแอตทริบิวต์ในคลาสและวิธีการของคุณคุณสามารถเขียนโค้ด REST URL โดยใช้รูปแบบ utc นี้คุณกำลังมีปัญหา (เห็นได้ชัดว่าเป็น ISO8601 ซึ่งน่าจะมาถึงโดยใช้ 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)

.... แม้ว่าจะใช้งานได้กับวันที่หนึ่ง (startDate) แต่ด้วยเหตุผลบางประการมันไม่ทำงานเมื่อ endDate อยู่ในรูปแบบนี้ ... ดีบักเป็นเวลาหลายชั่วโมงมีเพียงเงื่อนงำเท่านั้นที่เป็นข้อยกเว้นกล่าวว่ามันไม่ชอบโคลอน ":" (แม้ แม้ว่า web.config จะถูกตั้งค่าด้วย:

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

ดังนั้นให้สร้างรูปแบบวันที่อื่น (นำมาจาก polyfill สำหรับรูปแบบวันที่ ISO) และเพิ่มลงในวันที่ Javascript (สำหรับความกะทัดรัดแปลงเป็นนาทีเท่านั้น):

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

    }());
}

จากนั้นเมื่อคุณส่งวันที่ไปยังวิธี Web API 2 คุณสามารถแปลงจากสตริงเป็นวันที่:

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

ดังนั้น URL จะเป็น

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

Hanselman ให้ข้อมูลที่เกี่ยวข้องที่นี่:

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


ในวิธี WebAPI คุณสามารถมีพารามิเตอร์วันที่และเวลาเป็น DateTime ว่างได้ (DateTime? startDateString, DateTime? endDateDtring)
DotNet Fan

ขอบคุณที่กล่าวถึง toISOString - ที่ช่วยฉัน บริการ RESTful WCF ของฉันทำงานได้ดีกับวันที่สองวันใน URI ดังนั้นคุณจึงไม่จำเป็นต้องมีการแปลงวันที่ที่ซับซ้อนของคุณ บางทีมันอาจจะเป็นมุมกลับกับ Web API ที่ไม่ชอบโคลอนแม้จะมีการตั้งค่าคอนฟิก ... แปลก
Neville

@Simon endDateจะใช้งานได้หาก URL ของคำขอมีเครื่องหมายทับไปข้างหน้า น่าเสียดายที่ฉันจำไม่ได้ว่าฉันไปเจอข้อมูลนี้มาจากที่ใดและฉันไม่รู้วิธีการแก้ไข
Pooven

ผู้ใช้นาฬิกา 24 ชั่วโมงที่ต้องการใช้สิ่งนี้ควรเปลี่ยน hh เป็น HH ในรูปแบบวันที่
เข้า

1
นี่คือคำตอบที่ถูกต้อง StackOverflow หยุดลงคำตอบ!
mghaoui

9

ในฐานะทางเลือกอื่นที่คล้ายกับคำตอบของ sk ฉันสามารถส่งวันที่ที่จัดรูปแบบโดยDate.prototype.toISOString()ในสตริงการสืบค้นได้ นี่คือรูปแบบ ISO 8601 มาตรฐานและเป็นที่ยอมรับโดยตัวควบคุม. Net Web API โดยไม่มีการกำหนดค่าเส้นทางหรือการดำเนินการเพิ่มเติม

เช่น

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

1
ใช่ไหม? คุณช่วยยกตัวอย่างที่ใช้งานได้หรือไม่? ฉันทำวิธีแก้ปัญหาเดียวกันแล้วและไม่ได้ผล
anatol

@anatol คุณได้ผลลัพธ์อะไร? โค้ดที่ให้มาเป็นตัวอย่างการทำงานโดยมีเงื่อนไขล่วงหน้าที่dateObjectเป็นDateอ็อบเจ็กต์เริ่มต้น
Bondolin

สิ่งนี้น่าจะได้รับการโหวตขึ้นเล็กน้อย วิธีนี้แก้ปัญหาของฉันได้โดยเปลี่ยน UTC เป็น ISO Simples
Regianni

1
@Regianni ดีใจที่ช่วย :-)
Bondolin

สิ่งนี้ใช้ได้ผลสำหรับฉันโดยใช้stackoverflow.com/a/115034/1302730เพื่อรับวันที่ในรูปแบบ ISO
BugLover

7

นี่คือวิธีการแก้ปัญหาและแบบจำลองสำหรับการแก้ปัญหาที่เป็นไปได้ ใช้ Moment.js ในไคลเอนต์ของคุณเพื่อจัดรูปแบบวันที่แปลงเป็นเวลายูนิกซ์

 $scope.startDate.unix()

ตั้งค่าพารามิเตอร์เส้นทางของคุณให้ยาว

[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 วิธี Unix DateTime


4

เคยเป็นงานที่เจ็บปวด แต่ตอนนี้เราสามารถใช้ toUTCString ():

ตัวอย่าง:

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

ใส่ด้านล่างลงในคำขอโพสต์ Ajax

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

3

ตามความเป็นจริงการระบุพารามิเตอร์อย่างชัดเจนเป็น? date = 'fulldatetime' ทำงานได้อย่างมีเสน่ห์ ดังนั้นนี่จะเป็นวิธีแก้ปัญหาสำหรับตอนนี้: อย่าใช้จุลภาค แต่ใช้วิธี GET แบบเก่า


0

เนื่องจากฉันได้เข้ารหัสระบบปฏิบัติการ ISO-8859-1 รูปแบบวันที่ "dd.MM.yyyy HH: mm: sss" จึงไม่รู้จักสิ่งที่ได้ผลคือการใช้สตริง InvariantCulture

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

0

เมื่อดูรหัสของคุณฉันถือว่าคุณไม่มีความกังวลเกี่ยวกับ 'เวลา' ของวัตถุ DateTime ในกรณีนี้คุณสามารถส่งวันที่เดือนและปีเป็นพารามิเตอร์จำนวนเต็ม โปรดดูรหัสต่อไปนี้ นี่คือตัวอย่างการทำงานจากโครงการปัจจุบันของฉัน

ข้อดีคือ; วิธีนี้ช่วยให้ฉันหลีกเลี่ยงปัญหารูปแบบ DateTime และวัฒนธรรมที่เข้ากันไม่ได้

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

หากคุณสนใจที่จะดูส่วนหน้าเช่นกันโปรดอ่านโค้ดด้านล่าง น่าเสียดายที่เขียนด้วย Angular นี่คือวิธีที่ฉันส่ง DateTime เป็นพารามิเตอร์การค้นหาตามปกติในคำขอ 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;
}

0

ทางออกหนึ่งที่เป็นไปได้คือการใช้ Ticks:

เห็บยาวสาธารณะ {get; }

จากนั้นในวิธีการของคอนโทรลเลอร์:

DateTime สาธารณะ (เห็บยาว);

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.