OData / AspNetCoreOData

ASP.NET Core OData: A server library built upon ODataLib and ASP.NET Core
Other
457 stars 158 forks source link

OData 9.0 Returning 200 with Broken JSON #1345

Open sarangp93 opened 3 days ago

sarangp93 commented 3 days ago

Hello everyone,

I'm currently working with .NET 8, OData 9, EF Core 8, and PostgreSQL. When I make requests using certain OData queries that are valid in OData but incompatible with EF Core, the response I receive is broken JSON with 200 status code instead of a clear error message. Here are a couple of example queries that reproduce the issue:

?$expand=Orders($compute=Amount div 0 as test; $select = *;) ?$expand=AllMovies($filter=category/any(x: x eq null)) If anyone has suggestions or knows of a way to handle or catch these errors gracefully, I’d appreciate your input.

Capture2

julealgon commented 3 days ago

This happens when an error is found during serialization I think.

Can you try just manually materializing your query upfront in the controller to see what the underlying exception is? You should be able to inject ODataQueryOptions<T> into your action and remove the [EnableQuery] attribute, then manually apply that instance to your queryable. If you then call ToList on the result of that, you should get an exception at that point.

julealgon commented 3 days ago

Also seems similar to this:

sarangp93 commented 2 days ago

I created a custom attribute that inherits from EnableQueryAttribute and overrides the ValidateQuery method to manage OData errors using our standard error model. The issue arises with certain OData queries that are valid according to OData, pass ValidateQuery, and get translated into LINQ. When EF Core processes these queries, OData starts to create a response with a 200 OK status, but an exception occurs inside EF Core during execution. As a result, the response returns a 200 OK with the embedded error, which makes it difficult for my exception-handling middleware to properly catch it.

This is our CustomEnableQueryAttribute :

public class CustomEnableQueryAttribute : EnableQueryAttribute
{
    public override void ValidateQuery(HttpRequest request, ODataQueryOptions queryOptions)
    {
        try
        {
            base.ValidateQuery(request, queryOptions);
        }
        catch (ODataException e)
        {
            throw new BadRequestAxHttpException(errorMessage: "The query specified in the URI is not valid.", moreInfo: e.Message);
        }
    }
}

Here's an example of a query that triggers the issue:

?$expand=Products($filter=Orders/any(x: x eq null))

The resulting error message is:

Expression of type 'System.Object' cannot be used for parameter of type 'Entities.ProductEntity' of method 'Boolean Contains[ProductEntity](System.Linq.IQueryable`1[Entities.ProductEntity], Entities.ProductEntity)' (Parameter 'arg1')

When I try to handle this error in middleware, I get an additional issue:

Headers are read-only; response has already started.

Here’s the LINQ query that EF Core tries to process:

DbSet<ProductEntity>()
    .Where($it => $it.OrderEntity == null ? null : (bool?)$it.OrderEntity
        .Any(x => x == null) == (bool?)True)

To work around this, I tested removing [EnableQuery] and using .ToList() on the result. This returns a null response without any OData errors. It would be ideal if OData handled exceptions that occur specifically within EF Core execution.

julealgon commented 2 days ago

I've also ran into this behavior in the past several times and it is not great at all. Hopefully the team will be able to fix this and properly report the errors as expected.