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.
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.
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With