How to do PATCH properly in strongly typed languages based on Spring - example

后端 未结 4 1315
甜味超标
甜味超标 2020-12-23 16:10

According to my knowledge:

  • PUT - update object with its whole representation (replace)
  • PATCH - update object with given fiel
4条回答
  •  粉色の甜心
    2020-12-23 16:48

    I have had the same problem, so here are my experiences / solutions.

    I would suggest that you implement the patch as it should be, so if

    • a key is present with a value > the value is set
    • a key is present with an empty string > the empty string is set
    • a key is present with a null value > the field is set to null
    • a key is absent > the value for that key is not changed

    If you don't do that, you'll soon get an api which is hard to understand.

    So I would drop your first option

    Agree with the client that if he wants to remove a property he should send me an empty string (but what about dates and other non-string types?)

    The second option is actually a good option in my opinion. And that is also what we did (kind of).

    I'm not sure if you can make the validation properties work with this option, but then again, should this validation not be on your domain layer? This could throw an exception from the domain which is handled by the rest layer and translated into a bad request.

    This is how we did it in one application:

    class PatchUserRequest {
      private boolean containsName = false;
      private String name;
    
      private boolean containsEmail = false;
      private String email;
    
      @Length(max = 100) // haven't tested this, but annotation is allowed on method, thus should work
      void setName(String name) {
        this.containsName = true;
        this.name = name;
      }
    
      boolean containsName() {
        return containsName;
      }
    
      String getName() {
        return name;
      }
    }
    ...
    

    The json deserializer will instantiate the PatchUserRequest but it will only call the setter method for fields that are present. So the contains boolean for missing fields will remain false.

    In another app we used the same principle but a little different. (I prefer this one)

    class PatchUserRequest {
      private static final String NAME_KEY = "name";
    
      private Map fields = new HashMap<>();;
    
      @Length(max = 100) // haven't tested this, but annotation is allowed on method, thus should work
      void setName(String name) {
        fields.put(NAME_KEY, name);
      }
    
      boolean containsName() {
        return fields.containsKey(NAME_KEY);
      }
    
      String getName() {
        return (String) fields.get(NAME_KEY);
      }
    }
    ...
    

    You could also do the same by letting your PatchUserRequest extend Map.

    Another option might be to write your own json deserializer, but I haven't tried that myself.

    One could say that PATCH shouldn't be used in such example and I should use PUT to update my User.

    I don't agree with this. I also use PATCH & PUT the same way as you stated:

    • PUT - update object with its whole representation (replace)
    • PATCH - update object with given fields only (update)

提交回复
热议问题