Search code examples
c#asp.net-web-apiodata

OData Function with DateTimeOffset? parameter


I'm trying to implement an OData collection function that receives two DateTimeOffset? parameters (MinSentOn and MaxSentOn) and will return some summary information from an Orders table, but I'm having routing problems when I pass the time part of the DateTimeOffset, receiving an HTTP Error 500.0 - Internal Server Error directly from IIS, because it seems it's trying to reach a file and not the controller itself.

Error thrown by the IIS

This is my current OData configuration:

odataBuilder.Namespace = "D";
var fc =
    odataBuilder.EntityType<Order>().Collection
        .Function("ToExecutiveSummary")
        .Returns<ExecutiveSummary>();
fc.Parameter<DateTimeOffset?>("MinSentOn");
fc.Parameter<DateTimeOffset?>("MaxSentOn");

This is the function in my controller:

[HttpGet]
public async Task<IHttpActionResult> ToExecutiveSummary(DateTimeOffset? minSentOn, DateTimeOffset? maxSentOn, CancellationToken ct)
{
    return await _uow.ExecuteAndCommitAsync(async () =>
    {
        var query = _uow.Orders.Query();
        if (minSentOn != null) query = query.Where(e => e.SentOn >= minSentOn.Value);
        if (maxSentOn != null) query = query.Where(e => e.SentOn <= maxSentOn.Value);

        //  TODO    needs optimization, test only
        var executiveSummary =
            query.Select(e =>
                new ExecutiveSummary
                {
                    TotalOrders = query.Count(),
                    TotalProducts = query.Sum(ex => ex.Quantity),
                    TotalPharmacies = query.GroupBy(ex => ex.Pharmacy.Id, ex => ex.Pharmacy.Id).Count()
                }).FirstOrDefault();

        return Ok(executiveSummary);
    }, ct);
}

Snippet of the web.config changes to support the OData path and solve some routing problems I faced until I hit this wall, like dots or double escapes (changes have comments):

<configuration>

  <!--  ...     -->

  <system.web>
    <compilation debug="true" targetFramework="4.5.2" />

    <!--    Removed : and % from the path filter    -->
    <httpRuntime targetFramework="4.5.2" requestPathInvalidCharacters="&lt;,&gt;,*,&amp;,\,?"/>

    <globalization uiCulture="pt-PT" culture="pt-PT" />
  </system.web>

  <!--  ...     -->

  <system.webServer>
    <handlers>
      <remove name="ExtensionlessUrlHandler-Integrated-4.0" />
      <remove name="OPTIONSVerbHandler" />
      <remove name="TRACEVerbHandler" />

      <!--  to support the dot (.) for functions or actions     -->
      <add name="ExtensionlessUrlHandler-Integrated-4.0" path="/*" verb="*" type="System.Web.Handlers.TransferRequestHandler" preCondition="integratedMode,runtimeVersionv4.0" />
    </handlers>
    <security>

      <!--  to support double escapings, like 2015-08-10%2000:00:00.0000%2B01:00    -->
      <requestFiltering allowDoubleEscaping="true"/>
    </security>
  </system.webServer>

  <!--  ...     -->

</configuration>

Now, during my testings, I'm facing the following:

If I don't pass the time part (example: http://localhost:58806/odata/Order/D.ToExecutiveSummary(MinSentOn=null,MaxSentOn=2015-08-10) ) the request reaches my code without any problem, making me believe that there aren't any problem with the OData configuration and route. But when I include the time part (example: http://localhost:58806/odata/Order/D.ToExecutiveSummary(MinSentOn=null,MaxSentOn=2015-08-10%2000:00:00.0000%2B01:00) ) I receive an Internal Server Error (image attached) directly from the IIS. It seems it is trying to resolve to a file instead to the controller, whence the problem.

Ultimately, I know I could receive the parameters as strings and make the parse myself, but I would like to implement this without using the "hammer" :)


Solution

  • I finally found the problem! It was related to the DateTimeOffset format I was using. I was forgetting the T, and writing 2015-08-10 00:00:00.0000%2B01:00 instead of 2015-08-10T00:00:00.0000%2B01:00 and it couldn't parse correctly. What confused me was IIS throwing an Internal Server Error instead of some Bad Request or Not Found, and because the application exception handler wasn't being invoked, so I assumed the format was ok but IIS was having some problem with the path having unusual characters.

    @Sam Xu suggestion lead me in the right path, because even using alias (I didn't know OData had support for parameter alias - thanks for that!) it was still throwing the exception...

    In the end, it was a failure of mine, even if the server response should have been more enlightening...