Jquery – Possible to post ODataQueryOptions from the Http Request body

asp.net-web-api2jqueryodata

I'm implementing a Web API interface to support some fairly complex queries to run against it and have run up against an issue with the maximum request URI length.

The definition of my Web API method looks like this (using Automapper to perform the DTO projections):

public IQueryable<ReportModel> Get(ODataQueryOptions<Report> queryOptions)
{
     var query = DbContext.Query<Report>();

     return (queryOptions.ApplyTo(query) as IQueryable<Report>).WithTranslations().Project(MappingEngine).To<ReportModel>().WithTranslations();
}

My request consists of a dynamically built OData query including a potentially large number of 'Field eq Id' filters which are captured into the ODataQueryOptions parameter which is then applied to the IQueryable database context. For example:

http://example.com/api/Report?$filter=(Field1+eq+1%20or%20Field1+eq+5%20or%20Field1+eq+10%20or%20Field1+eq+15...

The problem is occurring once the length of the request URI reaches a certain limit. Any request with a URI length over that limit results in a 404 error. After some testing, this limit appears to be around the 2KB range (a URI with 2065 characters works fine, while one with 2105 fails using Chrome, IE, or FF).

The simple solution to this seems to be changing the request type from a GET to a POST request sending the search query across in the body as opposed to the URI. I'm running into some issues trying to get this working, however, as I can't seem to get the ODataQueryOptions object to populate correctly from the POST request. My Web API method now looks like this:

public IQueryable<ReportModel> Post([FromBody] ODataQueryOptions<Report> queryOptions)
{
      var query = DbContext.Query<Report>();

      return (queryOptions.ApplyTo(query) as IQueryable<Report>).WithTranslations().Project(MappingEngine).To<ReportModel>().WithTranslations();
}

As you can see, I'm trying to populate the query options from the body of the request as opposed to from the URI. To this point I haven't been able to get the ODataQueryOptions parameter to populate from the request, and the parameter results in being 'null'. If I remove the '[FromBody]' attribute, the query options object will populate correctly from the request URI, but the same URI length issue remains.

Here is an example of how I'm calling the method from the browser (using jQuery):

$.ajax({
       url: "/API/Report",
       type: "POST",
       data: ko.toJSON({
           '$filter': 'Field1+eq+1%20or%20Field1+eq+5%20or%20Field1+eq+10%20or%20Field1+eq+15...'
       }),
       dataType: "json",
       processData: false,
       contentType: 'application/json; charset=utf-8',
});

First, is it possible to do what I am trying to do here (Post ODataQueryOptions in the body of the request)? If so, am I building the POST request correctly? Is there anything else I'm missing here?

Best Answer

You can pass the raw string value of the query options in the post body, and construct a query option in the post method of the controller.

The code below is just for filter query option. You can add other query options the same way.

public IQueryable<ReportModel> Post([FromBody] string filterRawValue)
{
    var context = new ODataQueryContext(Request.ODataProperties().Model, typeof(Report));
    var filterQueryOption = new FilterQueryOption(filterRawValue, context);
    var query = DbContext.Query<Report>();
    return (filterQueryOption.ApplyTo(query) as IQueryable<Report>).WithTranslations().Project(MappingEngine).To<ReportModel>().WithTranslations();
}
Related Topic