Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to fit more than 29 parameters in an Http GET request

I am trying to write a Method in my Controller class that responds to an Http GET request. The application is an ASP .NET Core 2.0 API.

The method looks like this:

[HttpGet("GetObjects/{parameter1?}/{parameter2:decimal?}.../{parameter29:bool?}"]
public IActionResult(List<string> parameter1 = null, decimal? parameter2 = null, ..., bool? parameter29 = null) 
{

}

When I go to add the 30th parameter I get this Exception: OverflowException: Value was either too large or too small for a Decimal. Further details in the Image.

I have tried adding as a parameter a nullable list of strings, a nullable bool and a nullable decimal. They all work fine when I swap a parameter of the existing method.

I see this error as soon as I build my project rather than run/ use it. The only change I make between the working version and the failing one is to add /{parameter29:bool?} (or any other type of parameter) to the end of the HttpGet attribute´s route.

enter image description here

I am trying to get all entries of the database that pertains to this object. I check which parameters are not null and filter the list of entries accordingly. I then return a filtered list.

like image 769
axelrotter Avatar asked Dec 07 '22 15:12

axelrotter


1 Answers

That's indeed a bug in asp.net core routing. Looking at RoutePrecedence.cs we see:

// Compute the precedence for generating a url
// e.g.: /api/template          == 5.5
//       /api/template/{id}     == 5.53
//       /api/{id:int}          == 5.4
//       /api/template/{id:int} == 5.54
public static decimal ComputeOutbound(RouteTemplate template)
{
    // Each precedence digit corresponds to one decimal place. For example, 3 segments with precedences 2, 1,
    // and 4 results in a combined precedence of 2.14 (decimal).
    var precedence = 0m;

    for (var i = 0; i < template.Segments.Count; i++)
    {
        var segment = template.Segments[i];

        var digit = ComputeOutboundPrecedenceDigit(segment);
        Debug.Assert(digit >= 0 && digit < 10);

        precedence += decimal.Divide(digit, (decimal)Math.Pow(10, i));
    }

    return precedence;
}

This function computes precedence number for route template, so that routes can be sorted in order in which they should be evaluated. For example, /api/template/{id} should be evaluated before /api/template, because the former is more specific than the latter.

Specific details of how this is calculated are irrelevant for this question, so we can reduce it leaving only relevant part:

for (var i = 0; i < template.Segments.Count; i++) {
     ... (decimal)Math.Pow(10, i);
}

So for each segment of a route, Math.Pow(10, segmentIndex) is calculated and casted to decimal. Maximum possible value of decimal lies between Math.Pow(10, 28) and Math.Pow(10, 29). So as long as your route template contains 30+ parts - this code fails to compute its precedence value.

You can file an issue at asp.net core github repository, though I doubt it will have a high priority, because I doubt that routes with 30+ parts are ever used in real world scenarios.

Best solution would be to not use such a route. Use query string parameters, or json model POSTed in the body of request instead.

like image 177
Evk Avatar answered Jan 28 '23 10:01

Evk