可以从Http Request正文发布ODataQueryOptions吗? [英] Possible to post ODataQueryOptions from the Http Request body?

查看:93
本文介绍了可以从Http Request正文发布ODataQueryOptions吗?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在实现一个Web API接口,以支持一些相当复杂的查询来针对它运行,并且遇到了最大请求URI长度的问题.

我的Web API方法的定义如下(使用Automapper执行DTO投影):

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();
}

我的请求由一个动态构建的OData查询组成,该查询包含可能大量的"Field eq Id"过滤器,这些过滤器被捕获到ODataQueryOptions参数中,然后将其应用于IQueryable数据库上下文.例如:

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

一旦请求URI的长度达到某个限制,就会出现问题. URI长度超过该限制的任何请求都将导致404错误.经过一些测试后,该限制似乎在2KB范围内(使用2065个字符的URI可以正常工作,而使用Chrome,IE或FF的URI可以使用2105个字符失败).

一个简单的解决方案似乎是将请求类型从GET更改为POST请求,从而在正文中发送搜索查询,而不是URI.但是,由于无法似乎从POST请求正确填充ODataQueryOptions对象,因此我在尝试使其工作时遇到了一些问题.我的Web API方法现在看起来像这样:

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();
}

如您所见,我正在尝试从请求正文而非URI填充查询选项.到目前为止,我还无法从请求中获取ODataQueryOptions参数以进行填充,并且该参数导致结果为"null".如果我删除'[FromBody]'属性,则查询选项对象将从请求URI中正确填充,但是仍然存在相同的URI长度问题.

以下是我如何从浏览器(使用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',
});

首先,可以在这里做我想做的事情吗(将ODataQueryOptions张贴在请求的正文中)?如果是这样,我是否正确构建了POST请求?这里还有我想念的东西吗?

解决方案

您可以在帖子正文中传递查询选项的原始字符串值, 并在控制器的post方法中构造一个查询选项.

下面的代码仅用于过滤器查询选项. 您可以以相同的方式添加其他查询选项.

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();
}

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?

解决方案

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();
}

这篇关于可以从Http Request正文发布ODataQueryOptions吗?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆