如何传递 datetime 参数?

如何将 UTC 日期传递给 Web API?

传递 2010-01-01可以工作得很好,但是当我传递一个 UTC 日期(比如带有时间组件的 2014-12-31T22:00:00.000Z)时,我会得到一个 HTTP404响应。那么

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

产生404错误响应,而

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

挺好的。

那么,如何将 UTC 日期传递给 Web API ——或者至少指定日期 还有时间?

258421 次浏览

事实上,将参数明确指定为?约会 = “完整的约会时间”非常有效。因此,这将是目前的一个解决方案: 不使用逗号,但使用旧的 GET 方法。

我感受到你的痛苦... 又一个约会时间格式... 正是你需要的!

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

因此,使用类和方法上的属性,您可以使用这种 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';
};


}());
}

Then when you send the dates to the Web API 2 method, you can convert them from string to date:

[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

The problem is twofold:

1. 路线中的 .

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

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

  • Add a new handler specifically for the routes that must allow a dot. Be sure to add it 之前 the default. To do this, make sure you 拿开 the default handler first, and add it back after yours.
  • path="*."属性更改为 path="*"。然后它会捕捉到一切。请注意,从那时起,您的 web API 将不再将带点的来电解释为静态资源!如果你在你的 web 应用程序接口上托管静态资源,这是不建议的!
  • 将以下内容添加到 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,并对 URL 进行编码,因此用 %3A替换所有 :,例如使用 HttpUtility.UrlEncode()

这是一个解决方案,也是一个可能解决方案的模型。在客户机中使用 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 time 创建一个扩展方法

It used to be a painful task, but now we can use toUTCString():

例如:

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

将以下内容放入 Ajax 文章请求中

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

in your Product Web API controller:

[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 传递参数 use Moment.js

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

与 s k 的答案类似,我能够在查询字符串中传递一个由 Date.prototype.toISOString()格式化的日期。这是标准的 ISO8601格式,它是接受的。没有任何路由或操作的附加配置的 NetWebAPI 控制器。

例如:。

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

因为我已经编码了 ISO-8859-1操作系统,所以日期格式“ dd.MM.yyyy HH: mm: sss”没有被识别出是使用 InvariantCulture 字符串起了作用。

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

By looking at your code, I assume you do not have a concern about the 'Time' of the DateTime object. If so, you can pass the date, month and the year as integer parameters. Please see the following code. This is a working example from my current project.

这种方法的优点是: 可以帮助我避免 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 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;
}

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

公共长度标记{ get; }

然后在控制器的方法中:

公开日期时间(长勾) ;

将日期作为字符串传递,然后对其进行解析,这对我来说很有用。可能需要在解析器上添加 try catch,但这是基本代码。

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

Then the request can look like this

https://localhost/api/Contoller/name=test/date=2022-02-18T13:45:37.000Z

使用二进制格式。

to send the info in url use dateTimeVar.ToBinary() 就像是

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

当您收到的数据将得到像长变量,并使用静态函数的 DateTime 类转换为 DateTime 类型再次。

DateTimeMyDateTime = DateTime.FromBinary (BinaryDateTime) ;

干杯

对于外部 API (您不知道什么类型的客户端将调用您的服务) ,应该在输入参数和输出日期字段上使用 Unix Time。 Https://learn.microsoft.com/en-us/dotnet/api/system.datetimeoffset.tounixtimeseconds?view=net-6.0

. Net 提供 ToUnixtimeSecds 和 FromUnixtimeSecds,可以轻松地转换为 DateTime 或 DateTimeOff

Unix Time 应该优先于 ISO 格式,因为它只是一个整数,可以在 URL 字符串中传递而不需要编码。
‘ Ticks’属性类似于 Unix 时间,但是(我认为)应该只在。网络客户端和服务器。

最著名的 API 将使用 Unix Time,例如,参见 Stripe 的 API: Https://stripe.com/docs/api

使用 Unix 时间的明显缺点是:

  • 它们不是人类可读的
  • 它们不能由人类创建,因此在没有代码的情况下很难调用 API