Search code examples
c#asp.net-web-apiodataodata-v4

alphanumeric string in OData v4's function string parameter is parsed as int into string, or null


I'm having some problems passing an alphanumeric string as a parameter for an OData function. Here is its signature:

[EnableQuery]
    public IHttpActionResult GetForConstant([FromODataUri]string constant)

and its configuration:

var getForConstant = collection.Function("GetForConstant")
    .ReturnsFromEntitySet<BasicParameter>("Parameters");
getForConstant.Parameter<string>("constant");

and its metadata:

<Function Name="GetForConstant" IsBound="true">
    <Parameter Name="bindingParameter" Type="Collection(MyApp.BasicParameter)" />
    <Parameter Name="constant" Type="Edm.String" Unicode="false" />
    <ReturnType Type="MyApp.BasicParameter" />
</Function>

Here are different parsed values of the "constant" controller method parameter with the associated call:

http://xxx/api/Parameters/Default.GetForConstant(constant='123')
constant: "123"

http://xxx/api/Parameters/Default.GetForConstant(constant='999999999999999999999999999999999999999999999999')
constant: "1E+48"

http://xxx/api/Parameters/Default.GetForConstant(constant='12aa') 
constant: "12" 

http://xxx/api/Parameters/Default.GetForConstant(constant='aa')
constant: null 

http://xxx/api/Parameters/Default.GetForConstant(constant='aa12') 
constant: null

If I try without the single quotes, I get a 406 Not Acceptable error.

I tried updating WebAPI 2.2 for OData to the latest version (5.4.0) and also updated to the latest ODataLib (6.10.0) but that didn't help.

Any idea what's wrong?

If it matters, I'm mixing WebAPI and MVC (for help pages). Here are my routing configurations.

MVC:

public static void RegisterRoutes(RouteCollection routes)
    {
        routes.IgnoreRoute("{resource}.axd/{*pathInfo}");

        routes.MapRoute( //MapRoute for controllers inheriting from standard Controller
            name: "Default",
            url: "{controller}/{action}/{id}",
            defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional }
        );
    }

WebAPI:

public static void Register(HttpConfiguration config)
    {
        config.Routes.MapHttpRoute( //MapHTTPRoute for controllers inheriting ApiController
                name: "DefaultApi",
                routeTemplate: "api/{controller}/{id}",
                defaults: new { id = RouteParameter.Optional }
        );
    }

OData:

public static void Register(HttpConfiguration config)
    {
        config.MapHttpAttributeRoutes();

        var builder = new ODataConventionModelBuilder() { Namespace = "Default" };

*snip! model configurations here*

        var model = builder.GetEdmModel();
        config.MapODataServiceRoute("ODataRoute", "api", model);
    }

Solution

  • As per comment chain on the question, it seems like the manner in which the routing conventions are populating the function parameters is doing something strange. Since attribute based routing is the first that runs, using it seems to make sure "the right thing" happens.

    To narrow down the issue further will require debugging into the routing conventions to find which is executing for this scenario, and what it's doing to populate the route data.

    So meanwhile, adding the following attribute to the function works:

    [ODataRoute("/Parameters/Default.GetForConstant(constant={constant})"]