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

[Request] Add support for arbitrary models in custom actions #848

Open
julealgon opened this issue Feb 23, 2023 · 4 comments
Open

[Request] Add support for arbitrary models in custom actions #848

julealgon opened this issue Feb 23, 2023 · 4 comments
Assignees
Labels

Comments

@julealgon
Copy link
Contributor

Custom functions today are very cumbersome to use: they require the use of parameters that are bound to a custom dictionary-like structure, ODataActionParameters. The consumer then needs to read the parameters from this object to use them.

This goes against standard MVC model binding which allows (and recommends) binding your parameters directly into a strongly typed model object that represent them.

I think adding native support in OData to bind the body of the action to an actual custom model (like MVC does) would make the framework vastly easier to use.

From the docs: https://learn.microsoft.com/en-us/odata/webapi-8/fundamentals/actions-functions#bound-action-1

Instead of:

    [HttpPost("odata/Books({key})/Rate")]
    public IActionResult Rate([FromODataUri] string key, ODataActionParameters parameters)
    {
        if (!ModelState.IsValid)
        {
            return BadRequest();
        }

        int rating = (int)parameters["rating"];

        if (rating < 0)
        {
            return BadRequest();
        }

        return Ok(new BookRating() { BookID = key, Rating = rating });
    }

We'd do:

    public class RatingViewModel
    {
        public int Rating { get; set; }
    }

    [HttpPost("odata/Books({key})/Rate")]
    public IActionResult Rate([FromODataUri] string key, RatingViewModel ratingViewModel)
    {
        if (!ModelState.IsValid)
        {
            return BadRequest();
        }

        if (ratingViewModel.Rating < 0)
        {
            return BadRequest();
        }

        return Ok(new BookRating() { BookID = key, Rating = ratingViewModel.Rating });
    }

This would naturally also open the door for standard validation using DataAnnotations or FluentValidation, making it much more seamless when coming from a normal MVC route.

When specifying the EDM for the function, I'm not entirely sure what would need to be done there, but maybe something that would generate the required EDM automatically based on the complex type could be achieved?

    builder.EntityType<Book>()
        .Action("rate")
        .Parameter<RatingViewModel>("ratingViewModel");

The Parameter method could identify that the type is not a primitive type, and apply the rules as needed (either transparently transform it into multiple Parameter<primitiveType> calls for each property, or just support this natively at the EDM level as well.

@julealgon julealgon added the bug Something isn't working label Feb 23, 2023
@xuzhg
Copy link
Member

xuzhg commented Feb 27, 2023

@julealgon Actually, I had a rough design for a similar situation. Correct me at https://github.com/OData/AspNetCoreOData/blob/main/src/Microsoft.AspNetCore.OData/Formatter/FromODataBodyAttribute.cs

It's not finished yet until I have more clear customer usages. Any thoughts?

@xuzhg xuzhg self-assigned this Feb 27, 2023
@xuzhg xuzhg added feature and removed bug Something isn't working labels Feb 27, 2023
@julealgon
Copy link
Contributor Author

@julealgon Actually, I had a rough design for a similar situation. Correct me at https://github.com/OData/AspNetCoreOData/blob/main/src/Microsoft.AspNetCore.OData/Formatter/FromODataBodyAttribute.cs

It's not finished yet until I have more clear customer usages. Any thoughts?

That's certainly a start right there!

Personally, I think it would be awesome if we found a way to do it without requiring a custom attribute or at least a way to default to that attribute for action complex type parameters similar to how standard MVC routes default to [FromBody] for POST requests when using [ApiController].

I don't precisely know how the convention was created with [ApiController] but would suggest looking into it to see if we could define a similar convention for OData actions.

@julealgon
Copy link
Contributor Author

Forgot to link this with the other issue were we discussed about it and I mentioned I'd open it:

@habex-ch FYI

@cfauchere
Copy link

cfauchere commented Sep 17, 2024

How does FromODataBodyAttribute help? The action does not show in swagger and I get a 405 when I try it. If I remove the parameter from the model then the swagger is fine and I can call the function but the parameter is missing from $metadata

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

3 participants