1

I am building a RESTful API using the ServiceStack framework. A lot of the resources that I need to update are quite big, with up to 40 attributes per class, so I would like to do partial updates instead of replacing the entire resource. Often the client will only need to update one or two attributes out of the 40, so I would like to just send a JSON body consisting of the few attributes.

Since all combinations of attributes are possible, it is not feasible to make an "Update" class per class as suggested here: https://github.com/ServiceStack/ServiceStack/wiki/New-Api#patch-request-example

In the Microsoft ASP.NET WebAPI OData package there is a Delta class that takes a subset of a class and updates the resource based on this subset (http://www.strathweb.com/2013/01/easy-asp-net-web-api-resource-updates-with-delta/). This is the functionality I would like to have, as I will be having quite a few classes so a generic method would be best.

Basically, if I have a class

public class MyClass {
   public int a { get; set; }
   public int b { get; set; }
   ...
   public int z { get; set; }
}

I would like to update a resource of MyClass with a PATCH request with body

{"a":42,"c":42}

Is there a standard or recommended way to accomplish this with ServiceStack?

Daniel
  • 924
  • 1
  • 14
  • 31

2 Answers2

4

Declare any scalar values in your DTO as nullable. This will allow you to determine which fields were actually sent in the request:

public class MyClass {
    public int? a { get; set; }
    public int? b { get; set; }
    public int? c { get; set; }
    // etc.
    // object-type properties are already nullable of course
    public string MyString { get; set; }
}

Now if a client sends a partial request, like so:

{ "a": 1, "b": 0 }

You'll be able to determine which properties were actually sent when inspecting your DTO:

myClass.a == 1
myClass.b == 0
myClass.c == null
myClass.MyString == null
etc.

Set up a PATCH route for your DTO and implement a Patch method in your service:

public object Patch(MyClass request)
{
    var existing = GetMyClassObjectFromDatabase();
    existing.PopulateWithNonDefaultValues(request);
    SaveToDatabase(existing);
    ...
}

PopulateWithNonDefaultValues is key here. It will copy values from your request object onto the database entity, but will only copy properties that are not the default values. Thus, if a value is null, it won't copy it, because the client didn't send a value for it. Notice that it will copy an integer value of zero though, because we made it a nullable int, and the default value for a nullable int is considered by this method to be null, not zero. Declaring your DTO properties as nullable shouldn't cause much of a hassle in the rest of your code.

Note that this approach works easily with JSON. If you need to support XML requests/responses, you may need need to do some additional work with DataContract/DataMember attributes to insure that nulls are handled correctly.

Mike Mertsock
  • 11,825
  • 7
  • 42
  • 75
  • 5
    the problem with this approach is that if you want to actually null out a value, ie: {'a':null,'b'='some value'} you might want to actually set the value to null in the DB, thought? you might consider something like this: http://jsonpatch.com/ – Joshy Jul 04 '17 at 05:46
2

While esker's response is fine I would like to add that it might not be enough for nullable fields - since you don't know if the deserializer or the user have created that null field.

One approach would be to peek at the raw request.

A different approach is to ask the user to provide additional request (querystring) parameter to clearly specify which fields to patch. Something like: patch_fields=name,description,field3 The bonus of that approach is that the end user has more control over the patching and is not overriding a value by mistake (because he used the original entity and forgot to clear some fields)

Froyke
  • 1,115
  • 7
  • 13