如何传递datetime参数?


85

如何将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。– Nickolodeon

您可以启用调试以弄清楚为什么从传递的字符串到日期的转换失败吗?想法是弄清楚使用哪种方法将使用URL传递的日期转换为DateTime-我认为这是方法中的参数数据类型。
shahkalpesh 2013年

4
我会去做的。方法需要.NET DateTime参数。我认为无法通过时间组件并且找不到有关如何执行此操作的文档是荒谬的!
Nickolodeon

2
完成后发布解决方案。它可以帮助其他有类似问题的人。谢谢。
shahkalpesh 2013年

Answers:


33

问题是双重的:

1..路线中

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

我认为按照“正确性”的顺序可以有多种解决方案:

  • 为必须允许点的路由添加新的处理程序。确保在默认值之前添加它。为此,请确保先删除默认处理程序,然后再添加回默认处理程序。
  • path="*."属性更改为path="*"。然后它将捕获所有内容。请注意,从那时起,您的网络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从每一个请求。您仍然需要允许:的(CFR点2)。

您的“轻松解决方案”基本上为我完成了此任务,因为我不需要几秒钟。
内维尔

您是
拯救

1
在你的“简单的解决方案”,而不是允许的:S,我觉得你可以只使用%3A到位的:,它应该是罚款。
Mayer Spitzer

18

我感到您的痛苦……还有另一种约会时间格式……正是您所需要的!

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

因此,让我们制作另一个日期格式(从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;
    }

所以网址是

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

Hanselman在此处提供了一些相关信息:

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


在WebAPI方法中,您可以将datetime参数设置为可为null的DateTime(DateTime?startDateString,DateTime?endDateDtring)
DotNet Fan

感谢您提到toISOString-这救了我。我的RESTful WCF服务在URI中带有两个日期的情况下可以正常工作,因此不需要复杂的日期转换。尽管配置设置有点奇怪,但也许它具有Web API的怪癖却不喜欢冒号。
内维尔

@Simon,endDate如果请求网址包含尾随的正斜杠,则该方法将起作用。不幸的是,我不记得在哪里遇到了这些信息,也不清楚如何解决这个问题。
Pooven

想要使用此功能的24h时钟用户应将hh更改为日期格式的HH。
snaits

这是正确的答案。StackOverflow,停止拒绝答案!
mghaoui

18

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

8

作为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解决了我的问题。简单
Regianni

1
@Regianni很高兴它有所帮助:-)
Bondolin

这对我
有用

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 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:ssss”是使用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编写的。这就是我通常在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;
}

0

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

公共长Ti }

然后在控制器的方法中:

公共DateTime(长刻度);

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.