Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How can I modify an OData query to add access filters?

Tags:

I have a C# OData endpoint where I need to evaluate the filters that were submitted with the OData query in order to determine if I need to add additional filters to limit the results returned to the user.

My example model is simple:

Students -> Book Catalogs -> Books (All entities have a CampusId property assigned to them.)

When a user the belongs to Campus #5 performs the following query: "Students$select=Id,Name,CampusId" it should be transformed to: "Students$select=Id,Name,CampusId&$filter=CampusId eq 5"

I could do this with simple queries by simply adding a filter as a string.

What I really want to do is: 1) Determine the entities being selected and expanded 2) Determine if any of these entities have a CampusId property 3) Add the necessary filter values into the Uri so that the query for each selected and/or expanded entity is filtered to that Campus

I am trying to use Microsoft.OData.Core.UriParser.ODataUriParser to parse the filter values and then create a new Uri.

For example:

var parser = new Microsoft.OData.Core.UriParser.ODataUriParser(edmModel, new Uri(serviceRootPath), originalUri);
var filter = parser.ParseFilter();

Using the code snippet above you get the "filter" variable to provide a type of Microsoft.OData.Core.UriParser.Semantic.FilterClause which can be used to examine the current filter values in the OData Query Uri.

Does anyone know how to edit the values in the FilterClause to be able to add new filter values to the Uri?

I don't find many examples on how to edit the FilterClause in order to generate an updated Uri with new filter values.

like image 723
Klaus Barkhausen Avatar asked May 24 '16 21:05

Klaus Barkhausen


People also ask

How do I filter OData query?

You can use filter expressions in OData requests to filter and return only those results that match the expressions specified. You do this by adding the $filter system query option to the end of the OData request.

How do you use $expand in OData?

OData query option $expand is used to read multiple entities or entity sets in a single service call instead of two different calls. Prerequisite, entity sets which are used should be associated. To know about Association in OData service click here.

What is OData query parameters?

OData supports filter expressions for a broad range of primitives. The framework only supports the following subset of OData expressions: ◦ Expressions that use String, Int16, Int32, Int64, Boolean, DateTimeOffset, Single and Double types. ◦ Expressions that use comparison operators EQ, NE, GT, LT, GE, LE.


1 Answers

I found the solution to my isse by writing an algorithm that adds additional filtering to the request ODataUri based on the properties of my model. It examines any properties at the root level entity and the properties of any expanded entities as well to determine what additional filter expressions to add to the OData query.

OData v4 supports filtering in $expand clauses but the filterOption in the expanded entities is read only so you cannot modify the filter expressions for the expanded entities. You can only examine the filterOption contents at the expanded entities.

My solution was to examine all entities (root and expanded) for their properties and then add any additional $filter options I needed at the root filter of the request ODataUri.

Here is an example OData request Url:

/RootEntity?$expand=OtherEntity($expand=SomeOtherEntity)

This is the same OData request Url after I had updated it:

/RootEntity?$filter=OtherEntity/SomeOtherEntity/Id eq 3&$expand=OtherEntity($expand=SomeOtherEntity)

Steps I used to accomplish this:

  1. Use ODataUriParser to parse the incoming Url into a Uri object

See below:

var parser = new ODataUriParser(model, new Uri(serviceRootPath), requestUri);   
var odataUri = parser.ParseUri();
  1. Create a method that will traverse down from the root to all expanded entities and pass the ODataUri by ref (so that you can update it as needed as you examine each entity)

The first method will examine the root entity and add any additional filters based on the properties of the root entity.

AddCustomFilters(ref ODataUri odataUri);

The AddCustomFilters method will the traverse the expanded entities and call the AddCustomFiltersToExpandedEntity which will continue to traverse down all expanded entities to add any necessary filters.

foreach (var item in odatauri.SelectAndExpand.SelectedItems)
{
    AddCustomFiltersToExpandedEntity(ref ODataUri odataUri, ExpandedNavigationSelectItem expandedNavigationSelectItem, string parentNavigationNameProperty)
}

The method AddCustomFiltersToExpandedEntity should call itself as it loops over the expanded entities at each level.

  1. To update the root filter as you examine each entity

Create a new filter clause with your additional filter requirements and overwrite the existing filter clause at the root level. The $filter at the root level of the ODataUri has a setter so it can be overriden.

odataUri.Filter = new FilterClause(newFilterExpression, newFilterRange);

Note: I created a new filter clause using a BinaryOperatorKind.And so that any additional filter expressions are simply appended to any existing filter expressions already in the ODataUri

var combinedFilterExpression = new BinaryOperatorNode(BinaryOperatorKind.And, odataUri.Filter.Expression, newFilterExpression);
odataUri.Filter = new FilterClause(combinedFilterExpression, newFilterRange);
  1. Use ODataUriBuilder to create a new Url based on the updated Uri

See below:

var updatedODataUri = new Microsoft.OData.Core.UriBuilder.ODataUriBuilder(ODataUrlConventions.Default, odataUri).BuildUri();
  1. Replace the request Uri with the updated Uri.

This allows the OData controller to complete processing the request using the updated OData Url which includes the additional filter options you just added to the root level filer.

ActionContext.Request.RequestUri = updatedODataUri;

This has provided me with the capability to add any filtering options I need and be 100% sure that I have not altered the OData Url structure incorrectly.

I hope this helps someone else when facing this same issue.

like image 74
Klaus Barkhausen Avatar answered Sep 28 '22 04:09

Klaus Barkhausen