【问题标题】:How to pass a datetime parameter?如何传递日期时间参数?
【发布时间】:2013-01-16 13:28:13
【问题描述】:

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

【问题讨论】:

  • 日期中的“:”是嫌疑人吗?尝试逃避它。 http://domain/api/controller/action/2012-12-31T22%3A00%3A00.000Z
  • 转义没有帮助。还是 404。
  • 您能否启用调试以找出从传递的字符串到日期的转换失败的原因?这个想法是弄清楚使用什么方法将您使用 URL 传递的日期转换为 DateTime - 我假设这是您方法上参数的数据类型。
  • 我会这样做的。方法需要 .NET DateTime 参数。我认为我无法通过时间组件并且找不到有关如何做到这一点的文档,这很荒谬!
  • 完成后发布您的解决方案。它可以帮助其他有类似问题的人。谢谢。

标签: asp.net datetime asp.net-web-api


【解决方案1】:

问题是双重的:

1。路由中的.

默认情况下,IIS 将所有带有点的 URI 视为静态资源,并尝试将其返回并完全跳过进一步处理(通过 Web API)。这是在 system.webServer.handlers 部分的 Web.config 中配置的:默认处理程序处理 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="<,>,%,&,*,\,?" />。我已经从无效字符的标准列表中删除了:

更简单/更安全的解决方案

虽然不能回答您的问题,但更安全、更简单的解决方案是更改请求,以便不需要所有这些。这可以通过两种方式完成:

  1. 将日期作为查询字符串参数传递,例如?date=2012-12-31T22:00:00.000Z
  2. 从每个请求中删除.000。您仍然需要允许 :(参见第 2 点)。

【讨论】:

  • 您的“更简单的解决方案”基本上为我做到了,因为我不需要几秒钟。
  • 你是救生员 :)
  • 在您的“更简单的解决方案”中,我认为您可以使用%3A 代替: 而不是允许:s,应该没问题。
  • 允许: 的安全隐患是什么
【解决方案2】:

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

【讨论】:

    【解决方案3】:

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

    使用 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

    【讨论】:

    • 在 WebAPI 方法中,您可以将 datetime 参数设置为可为空的 DateTime (DateTime?startDateString, DateTime?endDateDtring)
    • 感谢提及 toISOString - 这救了我。我的 RESTful WCF 服务适用于 URI 中的两个日期,因此不需要复杂的日期转换。尽管有配置设置,但可能是 Web API 不喜欢冒号的怪癖......虽然很奇怪。
    • @Simon,如果请求 URL 包含尾部正斜杠,endDate 将起作用。不幸的是,我不记得我是在哪里看到这些信息的,也不知道有什么办法解决这个问题。
    • 24h 时钟用户如果想使用它,请将日期格式的 hh 更改为 HH。
    • 这是正确答案。 StackOverflow,停止投票!
    【解决方案4】:

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

    例如

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

    【讨论】:

    • 是吗?你能提供任何这样的例子吗?我做了同样的解决方法,但它不起作用。
    • @anatol 你得到什么结果?提供的代码是一个工作示例,前提条件是dateObject 是一个初始化的Date 对象。
    • 这可能应该被投票赞成。这通过将UTC更改为ISO解决了我的问题。简单的
    • @Regianni 很高兴它有帮助:-)
    • 这对我有用 stackoverflow.com/a/115034/1302730 来获取 ISO 格式的日期
    【解决方案5】:

    这是一个解决方案和可能解决方案的模型。在客户端中使用 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 DateTime Method

    【讨论】:

      【解决方案6】:

      以前是个很痛苦的任务,现在我们可以使用 toUTCString():

      例子:

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

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

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

      【讨论】:

        【解决方案7】:

        事实上,将参数显式指定为 ?date='fulldatetime' 非常有用。所以这将是目前的解决方案:不要使用逗号,而是使用旧的 GET 方法。

        【讨论】:

          【解决方案8】:

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

          public long Ticks { get; }

          然后在控制器的方法中:

          公共日期时间(长刻度);

          【讨论】:

            【解决方案9】:

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

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

            【讨论】:

              【解决方案10】:

              通过查看您的代码,我假设您不必担心 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;
              }
              

              【讨论】:

                【解决方案11】:

                将日期作为字符串传递,然后解析它对我有用。可能想在解析时添加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
                

                【讨论】:

                  【解决方案12】:

                  使用二进制格式。

                  要在 url 中发送信息,请使用 dateTimeVar.ToBinary() 会是这样的

                  http://domain/api/controller/action/637774955400000000

                  当你收到数据时,数据会变成 Long 变量,并使用 DateTime 类的静态函数再次转换为 DateTime 类型。

                  DateTime MyDateTime = DateTime.FromBinary(BinaryDateTime);

                  干杯

                  【讨论】:

                    猜你喜欢
                    • 1970-01-01
                    • 1970-01-01
                    • 1970-01-01
                    • 1970-01-01
                    • 1970-01-01
                    • 1970-01-01
                    • 1970-01-01
                    • 2019-04-29
                    • 1970-01-01
                    相关资源
                    最近更新 更多