0

My team and I are in the process of implementing a data-layer in our MVC5 project. We are using OData on the Web API/backend code. Everything works great until we start hitting character limitations on our query strings.

For example:

This works:

builder.Function("GetSalesTaxRate")
       .Returns<double>()
       .CollectionParameter<string>("IdentityField");

[HttpGet]
[ODataRoute("GetSalesTaxRate(IdentityField={ids})")]
public IHttpActionResult GetSalesTaxRate([FromODataUri] IEnumerable <string> ids)
{
    double rate = 5.6;  // Use a fake number for the sample.
    return Ok(rate);
}

http://localhost:123456/api/GetSalesTaxRate(IdentityField=['11-3011','11-2011'])

This does not work:

http://localhost:123456/api/GetSalesTaxRate(IdentityField=['11-3011,11-2011,11-9041,11-1011,11-3111,11-3021,11-9021,11-9032,11-9031,11-9039,11-9033,11-9161,11-9013,11-3031,11-9051,11-9061,11-9071,11-1021,11-3121'])

The error I receive is:

Bad request. HTTP Error 400. The request URL is invalid.

Failed to load resource: the server responded with a status of 400 (Bad Request)

I have tried to use client-side filtering via OData (using $filter mechanism), as well as server-side custom data controller filtering (by passing [FromODataUri] IEnumerable < string > as a parameter to the method). Both have limitations for my situation.

I have unsuccessfully tried the registry setting for [UrlSegmentMaxLength] from the stackoverflow answer here: Bad Request - Invalid URL web api

I have unsuccessfully tried these web.config settings:

<system.web><httpRuntime targetFramework="4.6.1" requestValidationMode="4.0" maxQueryStringLength="2097151" maxUrlLength="10999" relaxedUrlToFileSystemMapping="true" enable="true" />.....
<security><requestFiltering><requestLimits maxQueryString="2097151" maxUrl="10999"/>....

I am trying to get this to work on my local box before deploying to a web server on our network. How can I get this to work locally? How can I get this endpoint to accept the many "IdentityField" values I need to pass in? I have tried to pass it as IEnumerable < string > as well as just a single string. I run into limits on both.

Do I need to approach this differently? I have read to use POST methods, but I am unsure how to do that. I need the ability to pass up to 800 of these "IdentityField" strings to this endpoint (about 10,000 characters). Any suggestions or ideas? Thank you!

Community
  • 1
  • 1
AussieJoe
  • 1,285
  • 1
  • 14
  • 29
  • 1
    POST is definitely the preferred method if you need to change data. If you're just getting data, I think you need to rethink a pattern that requires you to pass 6k+ identifiers in a single URL. Is there something else that can help identify these items besides their ids? How is the user of your API getting these ids to pass to your service? Maybe they share common features that you can pass to the service and figure out which entities apply? – Heretic Monkey May 01 '17 at 19:59
  • @MikeMcCaughan Thanks Mike. Unfortunately these IDs are the top level attribute used to gather these. I do not need to change the data, only query and read it (GET). The Id's are being selected from a picker in the UI. – AussieJoe May 01 '17 at 20:17
  • I'm a bit surprised that there's a way, in a UI, to pick 6,703 items from a picker... Is this perhaps a case of a "select all" kind of thing? Because that could be better served by using a second endpoint to allow for that case. – Heretic Monkey May 01 '17 at 20:23
  • @MikeMcCaughan Good catch. It's actually 838 Id's (I updated my question). It totally equates to about 6703 characters. If I use $filter (client side), and logical operators ( or ), it grows even more to 10,055 characters needed. The user can search and query for these IDs. The scenario is when they "SELECT ALL" and hit submit. Sorry I've been staring at this for a long time :( – AussieJoe May 01 '17 at 20:28
  • @MikeMcCaughan I am unable to pass 20 of these Id's, and I need to be able to do atleast that.... – AussieJoe May 01 '17 at 20:35
  • 1
    You should look into POSTing then. URLs were just not meant to transport a lot of data. – Heretic Monkey May 01 '17 at 20:36
  • @MikeMcCaughan I agree Mike. That's why I am here asking this question. To understand what options are available and how to implement those. I too feel that POST will be the answer, but I am currently unsure. – AussieJoe May 01 '17 at 20:46
  • @MikeMcCaughan would you know where to look at OData Post examples with parameters and datasets like this? I see Post examples, but I dont see any Post examples with arrays as parameters? – AussieJoe May 01 '17 at 21:45
  • 1
    I'm not sure if it's such a problem any more, but maximum URL lengths also used to be different by browser as well as the server side technology, so even if you find a solution, it may be that someone uses IE or Opera and suddenly it's broken again if you use GET. – Craig H May 02 '17 at 13:47
  • @CraigH do you know of any good examples for using the POST verb with OData endpoints? I kind of figured GET isn't the way we should go but I am unsure what other options there are. – AussieJoe May 02 '17 at 15:00
  • 1
    @AussieJoe sorry, I've not used OData endpoints! – Craig H May 03 '17 at 07:07

1 Answers1

1

Try to change max request length. It's iis limitation

<configuration>
<system.web>
    <httpRuntime maxRequestLength="1048576" />
</system.web>

Alex Pashkin
  • 301
  • 4
  • 15