Làm cách nào để chuyển một tham số datetime?


86

Làm cách nào để chuyển ngày UTC sang Web API?

Việc vượt qua 2010-01-01hoạt động tốt, nhưng khi tôi vượt qua một ngày UTC chẳng hạn như 2014-12-31T22:00:00.000Z(với thành phần thời gian), tôi nhận được phản hồi HTTP 404. Vì thế

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

mang lại phản hồi lỗi 404, trong khi

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

hoạt động tốt.

Làm cách nào để chuyển ngày UTC sang Web API sau đó - hoặc ít nhất là chỉ định ngày giờ?


2
":" Trong ngày có phải là nghi phạm không? Hãy thử thoát khỏi nó. http://domain/api/controller/action/2012-12-31T22%3A00%3A00.000Z
shahkalpesh

2
Bỏ trốn không giúp được gì. Vẫn còn 404.
Nickolodeon

Bạn có thể kích hoạt tính năng gỡ lỗi để tìm ra lý do tại sao bản dịch từ chuỗi đã truyền sang ngày không thành công không? Ý tưởng là tìm ra phương pháp nào đang được sử dụng để dịch ngày bạn đã chuyển bằng cách sử dụng URL DateTime- mà tôi giả sử là kiểu dữ liệu của paramater trên phương thức của bạn.
shahkalpesh

4
Tôi sẽ làm việc đó. Phương thức yêu cầu tham số .NET DateTime. Tôi nghĩ thật nực cười khi tôi không thể vượt qua thành phần thời gian và không thể tìm thấy tài liệu về cách làm điều đó!
Nickolodeon

2
Đăng giải pháp của bạn khi bạn hoàn thành. Nó có thể giúp những người khác gặp vấn đề tương tự. Cảm ơn.
shahkalpesh

Câu trả lời:


33

Vấn đề là gấp đôi:

1. Trong .tuyến

Theo mặc định, IIS coi tất cả các URI bằng một dấu chấm trong đó là tài nguyên tĩnh, cố gắng trả lại nó và hoàn toàn bỏ qua quá trình xử lý tiếp theo (bằng API Web). Điều này được cấu hình trong Web.config của bạn trong phần system.webServer.handlers: trình xử lý mặc định xử lý path="*.". Bạn sẽ không tìm thấy nhiều tài liệu về cú pháp kỳ lạ trong paththuộc tính này (regex sẽ có ý nghĩa hơn), nhưng điều này rõ ràng có nghĩa là "bất kỳ thứ gì không chứa dấu chấm" (và bất kỳ ký tự nào từ điểm 2 bên dưới). Do đó, 'Không mở rộng' trong tên ExtensionlessUrlHandler-Integrated-4.0.

Theo ý kiến ​​của tôi, có nhiều giải pháp theo thứ tự 'đúng':

  • Thêm một trình xử lý mới dành riêng cho các tuyến đường phải cho phép dấu chấm. Hãy chắc chắn thêm nó trước mặc định. Để thực hiện việc này, trước tiên hãy đảm bảo bạn xóa trình xử lý mặc định và thêm lại trình xử lý sau của bạn.
  • Thay đổi path="*."thuộc tính thành path="*". Sau đó nó sẽ bắt mọi thứ. Lưu ý rằng từ đó trở đi, api web của bạn sẽ không còn diễn giải các cuộc gọi đến có dấu chấm là tài nguyên tĩnh! Nếu bạn đang lưu trữ các tài nguyên tĩnh trên api web của mình, điều này là không nên!
  • Thêm phần sau vào Web.config của bạn để xử lý tất cả các yêu cầu một cách vô điều kiện: theo <system.webserver>:<modules runAllManagedModulesForAllRequests="true">

2. Trong :lộ trình

Sau khi bạn đã thay đổi những điều trên, theo mặc định, bạn sẽ gặp lỗi sau:

Giá trị Request.Path có thể nguy hiểm đã được phát hiện từ máy khách (:).

Bạn có thể thay đổi các ký tự không được phép / không hợp lệ được xác định trước trong Web.config của mình. Dưới <system.web>, thêm những điều sau đây: <httpRuntime requestPathInvalidCharacters="&lt;,&gt;,%,&amp;,*,\,?" />. Tôi đã xóa ký tự :khỏi danh sách chuẩn các ký tự không hợp lệ.

Giải pháp dễ dàng hơn / an toàn hơn

Mặc dù không phải là câu trả lời cho câu hỏi của bạn, nhưng một giải pháp an toàn và dễ dàng hơn sẽ là thay đổi yêu cầu để tất cả điều này không bắt buộc. Điều này có thể được thực hiện theo hai cách:

  1. Chuyển ngày dưới dạng tham số chuỗi truy vấn, chẳng hạn như ?date=2012-12-31T22:00:00.000Z.
  2. Tách .000khỏi mọi yêu cầu. Bạn vẫn cần phải cho phép :(xem điểm 2).

"Giải pháp dễ dàng hơn" của bạn về cơ bản đã làm điều đó cho tôi vì tôi không cần vài giây.
Neville

Bạn là một người tiết kiệm cuộc sống :)
Moeez

1
Trong "Giải pháp dễ dàng hơn" của bạn, Thay vì cho phép :s, tôi nghĩ bạn chỉ có thể sử dụng %3Athay thế :và nó sẽ ổn.
Mayer Spitzer

20

trong bộ điều khiển API Web Sản phẩm của bạn:

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

thử nghiệm phương thức GetProductPeriodOrders trong Fiddler - Composer:

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

Định dạng DateTime:

yyyy-MM-ddTHH:mm:ss

javascript truyền tham số sử dụng moment.js

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

18

Tôi cảm thấy nỗi đau của bạn ... nhưng một định dạng ngày giờ khác ... chỉ là những gì bạn cần!

Sử dụng Web Api 2, bạn có thể sử dụng các thuộc tính tuyến đường để chỉ định các tham số.

vì vậy với các thuộc tính trên lớp và phương thức của bạn, bạn có thể mã hóa URL REST bằng định dạng utc này mà bạn đang gặp sự cố (rõ ràng là ISO8601 của nó, có lẽ đã đến khi sử dụng 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)

.... NHƯNG, mặc dù điều này hoạt động với một ngày (ngày bắt đầu), vì một số lý do, nó không hoạt động khi ngày cuối cùng ở định dạng này ... đã gỡ lỗi trong nhiều giờ, chỉ có manh mối là ngoại lệ cho biết nó không giống như dấu hai chấm ":" (thậm chí mặc dù web.config được đặt bằng:

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

Vì vậy, hãy tạo một định dạng ngày khác (được lấy từ polyfill cho định dạng ngày ISO) và thêm nó vào ngày Javascript (nói ngắn gọn, chỉ chuyển đổi tối đa vài phút):

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

    }());
}

Sau đó, khi bạn gửi ngày đến phương thức Web API 2, bạn có thể chuyển đổi chúng từ chuỗi thành ngày:

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

vì vậy url sẽ là

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

Hanselman đưa ra một số thông tin liên quan ở đây:

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


Trong phương pháp WebAPI bạn có thể có datetime thông số như DateTime nullable (DateTime startDateString, DateTime endDateDtring?)
DotNet Fan

Cảm ơn vì đã đề cập đến toISOString - điều đó đã cứu tôi. Dịch vụ WCF RESTful của tôi hoạt động tốt với hai ngày trong URI, vì vậy không cần chuyển đổi ngày phức tạp của bạn. Có lẽ đó là một điều khó hiểu với Web API không thích dấu hai chấm mặc dù cài đặt cấu hình ... kỳ lạ.
Neville

@Simon, endDatesẽ hoạt động nếu Url yêu cầu bao gồm dấu gạch chéo ở phía trước. Thật không may, tôi không thể nhớ lại nơi tôi đã xem thông tin này, cũng như không biết cách giải quyết vấn đề này.
Pooven

Người dùng đồng hồ 24h muốn sử dụng điều này nên thay đổi hh thành HH ở định dạng ngày.
snaits

Đây là câu trả lời chính xác. StackOverflow, DỪNG TẢI XUỐNG CÂU TRẢ LỜI!
mghaoui

8

Như một sự thay thế tương tự cho câu trả lời của sk, tôi có thể chuyển một ngày được định dạng bởi Date.prototype.toISOString()chuỗi truy vấn. Đây là định dạng ISO 8601 tiêu chuẩn và nó được chấp nhận bởi bộ điều khiển .Net Web API mà không cần bất kỳ cấu hình bổ sung nào của tuyến hoặc hành động.

ví dụ

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

1
Là nó? bạn có thể cung cấp bất kỳ ví dụ nào mà điều này hoạt động không? Tôi đã thực hiện cùng một cách giải quyết và nó không hoạt động.
anatol

@anatol bạn nhận được kết quả gì? Mã được cung cấp là một ví dụ hoạt động, với điều kiện trước dateObjectlà một Dateđối tượng được khởi tạo .
Bondolin

Điều này có lẽ nên được bình chọn lên một chút. Điều này đã giải quyết vấn đề của tôi bằng cách thay đổi UTC thành ISO. Simples
Regianni

1
@Regianni rất vui vì nó đã giúp :-)
Bondolin

Điều này làm việc cho tôi bằng cách sử dụng stackoverflow.com/a/115034/1302730 để lấy ngày ở định dạng ISO
BugLover

7

Đây là một giải pháp và một mô hình cho các giải pháp khả thi. Sử dụng Moment.js trong ứng dụng khách của bạn để định dạng ngày tháng, chuyển đổi sang thời gian unix.

 $scope.startDate.unix()

Thiết lập các thông số tuyến đường của bạn dài.

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

Tạo một phương thức mở rộng cho Unix time. Phương pháp DateTime Unix


4

Nó từng là một nhiệm vụ khó khăn, nhưng bây giờ chúng ta có thể sử dụng toUTCString ():

Thí dụ:

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

Đưa những điều dưới đây vào yêu cầu bài đăng Ajax

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

3

Trên thực tế, việc chỉ định các tham số một cách rõ ràng là? Date = 'fulldatetime' hoạt động như một sự quyến rũ. Vì vậy, đây sẽ là một giải pháp cho bây giờ: không sử dụng dấu phẩy, mà hãy sử dụng phương pháp GET cũ.


0

Vì tôi đã mã hóa hệ điều hành ISO-8859-1 nên định dạng ngày "dd.MM.yyyy HH: mm: sss" không được công nhận là gì khi sử dụng chuỗi InvariantCulture.

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

0

Bằng cách xem mã của bạn, tôi cho rằng bạn không quan tâm đến 'Thời gian' của đối tượng DateTime. Nếu vậy, bạn có thể chuyển ngày, tháng và năm dưới dạng tham số số nguyên. Vui lòng xem đoạn mã sau. Đây là một ví dụ làm việc từ dự án hiện tại của tôi.

Ưu điểm là; phương pháp này giúp tôi tránh các vấn đề về định dạng DateTime và sự không tương thích về văn hóa.

    /// <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ếu bạn cũng muốn xem giao diện người dùng, vui lòng đọc mã bên dưới. Thật không may, điều đó được viết bằng Angular. Đây là cách tôi thường truyền DateTime làm tham số truy vấn trong các yêu cầu 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

Một giải pháp khả thi là sử dụng Ticks:

công dài Bọ ve {get; }

Sau đó, trong phương thức của bộ điều khiển:

DateTime công khai (tích dài);

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.