reactiveui / refit

The automatic type-safe REST library for .NET Core, Xamarin and .NET. Heavily inspired by Square's Retrofit library, Refit turns your REST API into a live interface.
https://reactiveui.github.io/refit/
MIT License
8.63k stars 746 forks source link

Make fullstack C# development easier #1605

Open ysmoradi opened 12 months ago

ysmoradi commented 12 months ago

As development with blazor and maui growths, there will be more & more projects with C# in their both backend and frontend. In such a scenario, there could be a shared project that share common code between backend and frontend (for example dto classes).

In refit we use interface with some methods that have attributes such as Get, Post etc. We could put that interface in a shared project, so api controller can implement it and client can call it to achieve a perfect solution that make api calling somehow strongly typed! Like the old days of WCF services (": Example:

public interface ICategoryController // in shared project
{
    [Get("/Category/Get")]
    Task<IQueryable<CategoryDto>> Get();

    [Get("/Category/Get/{id}")]
    Task<CategoryDto> Get(int id, CancellationToken cancellationToken = default);

    [Post("/Category/Create")]
    Task<CategoryDto> Create(CategoryDto dto, CancellationToken cancellationToken = default);

    [Put("/Category/Update")]
    Task<CategoryDto> Update(CategoryDto dto, CancellationToken cancellationToken = default);

    [Delete("/Category/Delete/{id}")]
    Task Delete(int id, CancellationToken cancellationToken = default);
}

public partial class CategoryController : ICategoryController // in server
{
    [HttpGet]
    public IQueryable<CategoryDto> Get()
    {
        ...
    }

    [HttpGet("{id:int}")]
    public async Task<CategoryDto> Get(int id, CancellationToken cancellationToken)
    {
        ...
    }

    [HttpPost]
    public async Task<CategoryDto> Create(CategoryDto dto, CancellationToken cancellationToken)
    {
        ...
    }

    [HttpPut]
    public async Task<CategoryDto> Update(CategoryDto dto, CancellationToken cancellationToken)
    {
        ...
    }

    [HttpDelete("{id:int}")]
    public async Task Delete(int id, CancellationToken cancellationToken)
    {
        ...
    }
}

Sadly, this code is not working because the Get method that returns IQueryable has different signature in interface and implementation. This is just an example and there are more scenarios but luckily, most method signatures are compatible. I tried to change get method in interface to such a thing:

    [Get("/Category/Get")]
    Task<IQueryable<CategoryDto>> Get() => null;

So I won't have to implement that in api controller at server side and with this approach I can skip a few method signatures that are problematic while enjoying the strongly typed approach for the rest of the method signatures.

But because of this issue and its related PR, so a solution won't work at all.

I can understand that business requirements that @MariusVolkhart has mentioned, but I think we may not skip methods that have refit attributes such as Get and Post at all.

I think the first check could be if the method has refit attributes, then we can check if it has body or not so we can achieve this new design I've mentioned without breaking @MariusVolkhart's requirement.

image

Thanks for your great library ❤️

Mortana89 commented 3 months ago

Voting here, with idd Blazor becoming so popular, the strongly typing gets more important. I've tried it myself as well, but we return IActionResult, as we wrap our responses in a HttpOk or NotFound or... This doesn't allow us to link the interface atm