108

如何将 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 - 或者至少指定日期时间?

4

11 回答 11

41

问题是双重的:

1..路线中

默认情况下,IIS 将所有带有点的 URI 视为静态资源,并尝试将其返回并完全跳过进一步处理(通过 Web API)。这是在您的 Web.config 部分中配置的system.webServer.handlers:默认处理程序句柄path="*."。您不会找到太多关于此path属性中奇怪语法的文档(正则表达式会更有意义),但这显然意味着“任何不包含点的东西”(以及下面第 2 点中的任何字符)。因此名称中的“无扩展” ExtensionlessUrlHandler-Integrated-4.0

在我看来,按照“正确性”的顺序,可以有多种解决方案:

  • 专门为必须允许点的路由添加一个新的处理程序。请务必在默认值之前添加它。为此,请确保首先删除默认处理程序,然后将其添加回来。
  • path="*."将属性更改为path="*"。然后它将捕获所有内容。请注意,从那时起,您的 web api 将不再将带有点的来电解释为静态资源!如果您在 Web 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每个请求中删除。您仍然需要允许:'s (cfr 点 2)。
于 2015-10-26T12:04:03.630 回答
33

在您的产品 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();
        }
    }
}

在 Fiddler - Composer 中测试 GetProductPeriodOrders 方法:

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

日期时间格式:

yyyy-MM-ddTHH:mm:ss

javascript传递参数使用moment.js

const dateStart = moment(startDate).format('YYYY-MM-DDTHH:mm:ss');
const dateEnd = moment(endDate).format('YYYY-MM-DDTHH:mm:ss');
于 2017-03-09T12:59:52.317 回答
19

我感觉到你的痛苦......另一种日期时间格式......正是你所需要的!

使用 Web Api 2,您可以使用路由属性来指定参数。

因此,使用您的类和方法上的属性,您可以使用您遇到问题的这种 utc 格式编写一个 REST URL(显然是它的 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>

因此,让我们制作另一种日期格式(取自 ISO 日期格式的 polyfill)并将其添加到 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;
    }

所以网址是

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

Hanselman 在这里提供了一些相关信息:

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

于 2014-03-01T05:22:54.870 回答
12

作为 s k 答案的类似替代方法,我可以Date.prototype.toISOString()在查询字符串中传递一个由 格式化的日期。这是标准的 ISO 8601 格式,它被 .Net Web API 控制器接受,无需对路由或操作进行任何额外配置。

例如

var dateString = dateObject.toISOString(); // "2019-07-01T04:00:00.000Z"
于 2019-07-02T12:22:42.190 回答
7

这是一个解决方案和可能解决方案的模型。在客户端中使用 Moment.js 格式化日期,转换为 unix 时间。

 $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 日期时间方法

于 2016-02-19T20:18:38.807 回答
4

这曾经是一项痛苦的任务,但现在我们可以使用 toUTCString():

例子:

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

将以下内容放入 Ajax 发布请求中

data: {
    Start: new Date().toUTCString(),
    End: new Date().toUTCString()
},
于 2016-09-17T08:01:37.707 回答
3

事实上,将参数明确指定为 ?date='fulldatetime' 就像一个魅力。所以现在这将是一个解决方案:不要使用逗号,而是使用旧的 GET 方法。

于 2013-01-16T15:30:48.043 回答
3

一种可能的解决方案是使用 Ticks:

公共长滴答{得到; }

然后在控制器的方法中:

公共日期时间(长滴答声);

于 2020-07-20T08:09:43.420 回答
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 编写的。这就是我通常在 Angular GET 请求中将 DateTime 作为查询参数传递的方式。

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;
}
于 2020-05-12T01:39:27.667 回答
0

由于我对 ISO-8859-1 操作系统进行了编码,因此无法识别日期格式“dd.MM.yyyy HH:mm:sss”的工作是使用 InvariantCulture 字符串。

string url = "GetData?DagsPr=" + DagsProfs.ToString(CultureInfo.InvariantCulture)
于 2020-04-24T00:54:55.197 回答
0

将日期作为字符串传递,然后解析它对我有用。可能想在解析中添加try catch,但这是基本代码。

[HttpGet("name={name}/date={date}", Name = "GetByNameAndDate")]
public IActionResult GetByNameAndDate(string name, string date) {
    DateTimeOffset dto = DateTimeOffset.Parse(date);
}

然后请求看起来像这样

https://localhost/api/Contoller/name=test/date=2022-02-18T13:45:37.000Z
于 2022-02-18T15:12:28.557 回答