Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Query options ignored for POST/PUT/PATCH with 'CreatedODataResult' and 'UpdatedODataResult' return types #434

Closed
julealgon opened this issue Jan 10, 2022 · 1 comment · Fixed by #981

Comments

@julealgon
Copy link
Contributor

  • Latest affected version: Microsoft.AspNetCore.OData v8.0.5

I was playing around with a sample API on my side and noticed that when I used either .Created or .Updated methods from ODataController as the return values for POST/PUT/PATCH operations, $select projections did not work at all.

Example:

    [EnableQuery]
    [HttpPut("{key}")]
    public ActionResult<WeatherForecast> Put([FromODataUri] Guid key, [FromBody] WeatherForecast update)
    {
        ...
        var update = MyRepository.Update(key, update)
        ...

        return Updated(update);
    }
  • PUT /api/forecasts(a84f1de3-5c2b-42d3-bc52-b6519e51e6af)?$Select=Summary with Prefer: return=representation

The projection is completely ignored and I get a 200 response with the full payload.

If I change the return from Created(entity)/Updated(entity) to just Ok(entity):

    [EnableQuery]
    [HttpPut("{key}")]
    public ActionResult<WeatherForecast> Put([FromODataUri] Guid key, [FromBody] WeatherForecast update)
    {
        ...
        var update = MyRepository.Update(key, update)
        ...

        return update;
    }

Then the projection suddenly works for the exact same request. Artificially changing to SingleResult also works just fine. Obviously, none of those are acceptable workarounds since one has to give up on the Location header and payload control functionalities.

Attempting to use both .Created/.Updated with SingleResult results in an exception (probably expected, but I figured I'd give it a try):

    [EnableQuery]
    [HttpPut("{key}")]
    public ActionResult<SingleResult<WeatherForecast>> Put([FromODataUri] Guid key, [FromBody] WeatherForecast update)
    {
        ...
        var update = MyRepository.Update(key, update)
        ...

        return Updated(SingleResult.Create(new[] { update }.AsQueryable()));
    }

System.Runtime.Serialization.SerializationException: 'SingleResult1' cannot be serialized using the OData output formatter. at Microsoft.AspNetCore.OData.Formatter.ODataOutputFormatterHelper.GetSerializer(Type type, Object value, HttpRequest request, IODataSerializerProvider serializerProvider) at Microsoft.AspNetCore.OData.Formatter.ODataOutputFormatterHelper.WriteToStreamAsync(Type type, Object value, IEdmModel model, ODataVersion version, Uri baseAddress, MediaTypeHeaderValue contentType, HttpRequest request, IHeaderDictionary requestHeaders, IODataSerializerProvider serializerProvider) at Microsoft.AspNetCore.OData.Results.UpdatedODataResult1.ExecuteResultAsync(ActionContext context)
at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.g__Logged|22_0(ResourceInvoker invoker, IActionResult result)
at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.g__Awaited|30_0[TFilter,TFilterAsync](ResourceInvoker invoker, Task lastTask, State next, Scope scope, Object state, Boolean isCompleted)
at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.Rethrow(ResultExecutedContextSealed context)
at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.ResultNext[TFilter,TFilterAsync](State& next, Scope& scope, Object& state, Boolean& isCompleted)
at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.InvokeResultFilters()
--- End of stack trace from previous location ---
at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.g__Awaited|20_0(ResourceInvoker invoker, Task lastTask, State next, Scope scope, Object state, Boolean isCompleted)
at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.g__Logged|17_1(ResourceInvoker invoker)
at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.g__Logged|17_1(ResourceInvoker invoker)
at Microsoft.AspNetCore.Routing.EndpointMiddleware.g__AwaitRequestTask|6_0(Endpoint endpoint, Task requestTask, ILogger logger)
at Microsoft.AspNetCore.Authorization.AuthorizationMiddleware.Invoke(HttpContext context)
at Microsoft.AspNetCore.OData.Routing.ODataRouteDebugMiddleware.Invoke(HttpContext context)
at Microsoft.AspNetCore.Diagnostics.DeveloperExceptionPageMiddleware.Invoke(HttpContext context)

.Created and .Updated should work as per the spec with EnableQuery so that one can manipulate the return payload using the query.

@Yglioh
Copy link

Yglioh commented Jan 26, 2022

Got the same issue as I would like to avoid making a second call to get the specific content of a created or updated entity.
I believe that the error stack trace given by @julealgon indicates the actual underlying issue, i.e. serializing.

In addition to that, when making use if Created/CreatedODataResult or Updated/UpdatedODataResult execution will fail earlier as the internal methods GenerateLocationHeader and GenerateEntityId cannot handle an IQueryable object.

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 a pull request may close this issue.

2 participants