Skip to content

Conversation

gathogojr
Copy link
Contributor

Issues

This pull request fixes #3066.

Description

This pull request addresses #3066 by aligning the default behavior of ODataUriResolver with the OData V4.01 specification.

According to the OData V4.01 specification, services MUST support case-insensitive system query option names, whether or not they are prefixed with $.

ASP.NET Core OData already complies with this requirement by injecting a case-insensitive UnqualifiedODataUriResolver (derived from ODataUriResolver) by default:
https://github.com/OData/AspNetCoreOData/blob/1368836752963d88e920d9508d94807eb995894b/src/Microsoft.AspNetCore.OData/ODataOptions.cs#L326-L329

To ensure consistent behavior across the OData ecosystem, this PR proposes setting EnableCaseInsensitive = true by default when initializing a new instance of ODataUriResolver in ODL:

private static readonly ODataUriResolver Default = new ODataUriResolver();

With this change, both of the following requests will be parsed successfully and equivalently:

GET https://localhost:54001/odata/Books?$expand=Authors
GET https://localhost:54001/odata/Books?$expand=authors

This change improves spec compliance and developer experience by reducing case-sensitivity issues in query options.

Checklist (Uncheck if it is not completed)

  • Test cases added
  • Build and test with one-click build and test script passed

Additional work necessary

If documentation update is needed, please add "Docs Needed" label to the issue and provide details about the required document change in the issue.

Repository notes

Team members can start a CI build by adding a comment with the text /AzurePipelines run to a PR. A bot may respond indicating that there is no pipeline associated with the pull request. This can be ignored if the build is triggered.

Team members should not trigger a build this way for pull requests coming from forked repositories. They should instead trigger the build manually by setting the "branch" to refs/pull/{prId}/merge where {prId} is the ID of the PR.

@gathogojr gathogojr force-pushed the odl-9/3066-odatauriresolver-enablecaseinsensitive-default-true branch from f31e27d to 7ff15ce Compare June 12, 2025 11:11
/// <remarks>
/// All extensions should look at this property and keep case sensitive behavior consistent.
/// </remarks>
public virtual bool EnableCaseInsensitive { get; set; }
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

From your notes in the PR, it says that the case insensitive for the query options key name no matter whether it prefixes with '$', Is it right?

Here, if we default to 'true', it means all the Uri parts are case insensitive by default?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@xuzhg That statement is directly from the standard: OData V4.01 states that services MUST support case-insensitive system query option names specified with or without the $ prefix.

With this change, the URI parts are case-insensitive by default - which is what the UnqualifiedODataUriResolver does in ASP.NET Core OData here:
https://github.com/OData/AspNetCoreOData/blob/1368836752963d88e920d9508d94807eb995894b/src/Microsoft.AspNetCore.OData/ODataOptions.cs#L326

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hi @gathogojr,

Could you clarify why this MR changes the handling of property names? The standard specifies that query parameter names (like $expand) should be case-insensitive, but not their values. For example, $exand and $EXPand should be treated as equivalent, but $expand=shoe and $expand=Shoe should not.

This has been a pain point for me regarding case insensitivity—especially since C# class properties are also resolved in a case-insensitive manner, though I haven’t found a clear justification for it. (Related PR where I struggled to write tests)

Could you explain why property names should be handled case-insensitively?

/// All extensions should look at this property and keep case sensitive behavior consistent.
/// </remarks>
public virtual bool EnableCaseInsensitive { get; set; }
public virtual bool EnableCaseInsensitive { get; set; } = true;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I thought this was already true by default.

Could you add a test that makes this to false.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@WanjohiSammy The default was false, as you can see from the following tests:
image

For the UnqualifiedODataUriResolver in ASP.NET Core OData here
https://github.com/OData/AspNetCoreOData/blob/1368836752963d88e920d9508d94807eb995894b/src/Microsoft.AspNetCore.OData/ODataOptions.cs#L326, we set it to true to support case insensitive behaviour when resolving OData URIs.

Regarding your ask, do you need me to add a test like the following:

        [Fact]
        public void TestEnableCaseInsensitiveBuiltinIdentifierSetToFalse()
        {
            Assert.False((new ODataUriResolver { EnableCaseInsensitive = false }).EnableCaseInsensitive);
        }

Copy link
Member

@WanjohiSammy WanjohiSammy Jun 16, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Something like this will suffice

WanjohiSammy
WanjohiSammy previously approved these changes Jun 16, 2025
@gathogojr gathogojr merged commit 81b6e39 into OData:dev-9.x Jun 24, 2025
2 checks passed
@gathogojr gathogojr deleted the odl-9/3066-odatauriresolver-enablecaseinsensitive-default-true branch June 24, 2025 06:18
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants