Search code examples
c#asp.net-web-apiodata

Supporting ODataQueryOptions in existing Web API


I have a Web API project which has been used for several years without OData support, just with standard URL params.

I now wish to add OData support to this API, but as the API is not built on a queryable model the intention is to receive the ODataQueryOptions<T> object and pass this down to a repository.

Everything I can find to read about supporting OData either assumes that I have a queryable model or is overly simplistic and simply tells me how to make sense of the ODataQueryOptions object. Consequently I'm unable to get a simple method up and running.

Here's what I currently have.

[Route("test")]
[HttpGet]
[EnableQuery]
public IHttpActionResult Test(ODataQueryOptions<TestOptions> options)
{
    var settings = new ODataValidationSettings {
            AllowedFunctions = AllowedFunctions.None,
            AllowedLogicalOperators = AllowedLogicalOperators.Equal,
            AllowedArithmeticOperators = AllowedArithmeticOperators.None,
            AllowedQueryOptions = AllowedQueryOptions.Filter
        };
    try
    {
        options.Validate(settings);
    }
    catch (ODataException exception)
    {
        return BadRequest(exception.Message);
    }

    var binaryOperator = options.Filter?.FilterClause?.Expression as BinaryOperatorNode;
    if (binaryOperator != null)
    {
        var property = binaryOperator.Left as SingleValuePropertyAccessNode ?? binaryOperator.Right as SingleValuePropertyAccessNode;
        var constant = binaryOperator.Left as ConstantNode ?? binaryOperator.Right as ConstantNode;

        if (property?.Property != null && constant?.Value != null)
        {
            ;
        }
    }

    return Ok();
}

The TestOptions class (in the ODataQueryOptions<TestOptions> param) is currently an empty class:

public class TestOptions
{
}

I've also added

public static class WebApiConfig
{
    public static void Register(HttpConfiguration config)
    {
        // existing code

        config.AddODataQueryFilter();
    }
}

However, upon calling this from a REST API client...

{
"Message": "An error has occurred.",
"ExceptionMessage": "No non-OData HTTP route registered.",
"ExceptionType": "System.InvalidOperationException",
"StackTrace": " ... "
}

What have I missed? I would have thought that I might have to register OData-enabled methods in the global.asax or similar, but the exception implies that the problem is with non-OData methods, but all the other methods still return as expected (i.e., without any OData involvement).


Solution

  • Actually, this works perfectly without EntityDate or any other model setup. You just need a List<Poco.Language> that you can convert with .AsQueryable() and off you go.

    [Route(""), HttpGet]
    public IHttpActionResult  Get(ODataQueryOptions<Poco.Language> queryOptions)
    {          
        return Ok(queryOptions.ApplyTo(_repository.GetAll().AsQueryable()));
    }
    

    Above controller can be called with all types of OData query options, normal routes and no setup in the WebApiConfig.

    Poco.Language is just a plain C# POCO class.