OData / AspNetCoreOData

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

Modify OData API response after OData is executed #614

Open jiangyi1985 opened 2 years ago

jiangyi1985 commented 2 years ago

How do I modify the result data of an OData API?

For example, in the response of a list OData API, we want to set some property to null.

I tried to do it in IResultFilter.OnResultExecuted like this:

    public class MyResultFilter : Attribute, IResultFilter
    {
        public void OnResultExecuted(ResultExecutedContext context)
        {
            var result = context.Result as OkObjectResult;
            var data = result.Value as EntityQueryable<Microsoft.AspNetCore.OData.Query.Wrapper.SelectAllAndExpand<DbContext.Table1>>;

            //modify data
        }
    }

    [EnableQuery]
    [MyResultFilter]
    public IActionResult Get()
    {
        var querable = DbContext.Table1.AsQueryable<Table1>();
        return Ok(querable);
    }

But the SelectAllAndExpand is internal so the code won't compile.

Is there a better way to do this?

julealgon commented 2 years ago

...in the response of a list OData API, we want to set some property to null.

Would you mind elaborating a bit on why you need such weird requirement?

If you don't want your API surface to reflect your internal model but something else (like a DTO), then I'd suggest using AutoMapper.Extensions.OData to achieve that by decoupling the models and performing the mapping you want.

ElizabethOkerio commented 2 years ago

@jiangyi1985 if what you are doing is something simple then you can make the changes in the controller method. Otherwise, you need to create a custom serializer and serializer provider to set the properties that you want set to null. You can look at this example on how to create custom serializers.

https://github.com/xuzhg/WebApiSample/tree/main/CustomSerializerSample

jiangyi1985 commented 2 years ago

...in the response of a list OData API, we want to set some property to null.

Would you mind elaborating a bit on why you need such weird requirement?

If you don't want your API surface to reflect your internal model but something else (like a DTO), then I'd suggest using AutoMapper.Extensions.OData to achieve that by decoupling the models and performing the mapping you want.

We are just trying to remove some sensitive info, depending on some condition.

For example, for the result of an OData API of a list of user reviews:

foreach(var r in reviews)
{
    if(r.IsAnonymousSubmission)
        r.SubmitterName = "";
}

We tried adding AutoMapper config CreateMap<Table1, Table1> and then in our action: return Ok(DbContext.Table1.GetQuery(_mapper, options));

But we got an exception: System.ArgumentException: 'Member virtual_property, does not exists in type DbContext.Table1.' where virtual_property is an EF relationship property on Table1

Does AutoMapper work if we have EF Relationship and we are using OData expand? Or if it's a config problem?

jiangyi1985 commented 2 years ago

@jiangyi1985 if what you are doing is something simple then you can make the changes in the controller method. Otherwise, you need to create a custom serializer and serializer provider to set the properties that you want set to null. You can look at this example on how to create custom serializers.

https://github.com/xuzhg/WebApiSample/tree/main/CustomSerializerSample

thanks we will look into this solution

julealgon commented 2 years ago

Does AutoMapper work if we have EF Relationship and we are using OData expand? Or if it's a config problem?

It does work with expands on the mapped type. I've never made a X to X mapping with this setup though, not sure if that could be the cause of your issue.

Have you considered creating a separate DTO model without the sensitive info in it and mapping to that instead?