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.59k stars 746 forks source link

Is there any way of DTO entity partial update (particular fields) on PUT? #560

Open agat366 opened 6 years ago

agat366 commented 6 years ago

I am not sure if I want to much, but still, is there any way to achieve partial update of DTO models?

For instance, if I have a DTO:

public class MyDto { public int Id {get;set;} public string Field1 {get;set;} public string Field2 {get;set;} public string Field3 {get;set;} }

sometimes I might need to send only one of the Fields to update (only Field1, for instance).

But when I use [Put("/myapi/{id}")] Task SavePartial([Body] MyDto data); all the fields are sent to the server, so if they are even null, some servers just update those values to "null" (or even throw array).

So, is there any way to send only needed data. I understand, that that's kind of quite custom logic, but in multi-technologies world, that is very handy sometimes. I imagine that that can be implemented in way of passing dynamic (anonymous) classes, or via Expressions when enumerating the fields to pass. There possibly already some sort of functionality implemented to support that in Refit. If not, what easy way it can be extended to achieve the goal mentioned?

xljiulang commented 6 years ago

well, json patch is what you need, it's supported in WebApiClient:

public interface IMyWebApi : IHttpApi
{
    [HttpPatch("webapi/user")]
    Task<UserInfo> PatchAsync(string id, JsonPatchDocument<UserInfo> doc);
}

var doc = new JsonPatchDocument<UserInfo>();
doc.Replace(item => item.Account, "laojiu");
doc.Replace(item => item.Email, "laojiu@qq.com");
var client = HttpApiClient.Create<IMyWebApi>();
await client.PatchAsync("id001", doc);

in asp.net server:

[HttpPatch]
public async Task<UserInfo> Patch(string id, [FromBody] JsonPatchDocument<UserInfo> doc)
{
    var user = await GetUserInfoFromDbAsync(id);
    doc.ApplyTo(user);
    // save user to db ..
    return user;
}
agat366 commented 6 years ago

Thanks for the response, and the approach you've offered is great indeed, however, in my case this is not an option, as the api I use is kind of "public" one (at least, from the project architecture perspective) (and if to touch details, the backend is not even .NET, so it lives on its own rules). So I just should "remove" particular dto properties to post (otherwise the server just fails). Currently I am creating a separate dto per operation per item, like so:

public class Vehicle_Create_Dto
{
    public string Model { get; set; }
    public string Vin { get; set; }
}

Which is far not handy option. So, ideal solution for me would be sending kind of "dynamic" approach. So, do I have any other options in this case?

natelaff commented 4 years ago

Was having issues implementing Patch with Refit and ASP.NET Core, so I thought I'd offer some help in case anyone else runs into this.

[Patch("{id}")]
[Headers("Content-Type: application/json-patch+json")]
Task<UserInfo> PatchAsync(string id, JsonPatchDocument<UserInfo> doc);

You'll notice the addition of the Content-Type header to use json-patch, which is what was required in this case. Hope it helps someone :)