如何在Spring的@RequestBody中查找JSON字段是否包含数据?

huangapple go评论107阅读模式
英文:

How to find out, if json field has data or not with @RequestBody in Spring?

问题

让我们假设我有一个UserDTO。

@Getter
@Setter
public class UserDTO {
  private int id;
  private String name;
  private Integer salary;
  private Integer department_id;
}

我想通过部分更新的方式更新用户信息,使用PATCH请求,发送一个人的id(必需),以及其他部分可以发送的数据。

假设在我的数据库中有一个用户(2, "Dave", 10, null)。我发送一个请求,请求的主体是一个简单的JSON:
{"id":2, "salary":20}

我想要使用这些信息来更新关于这个人的部分信息,而不是全部信息。问题是,反序列化器会在缺失的字段中放置null值,换句话说,它将我的JSON视为:
{"id":2, "salary":20, "name":null, "department_id":null}

我猜这不是我想要的。直接的映射无法完成这项工作,因为它将缺失的字段视为空字段,这不同于一个空盒子和它的缺席。这让我认为将请求的主体放在UserDTO中不是一个好决定。

所以,即使是从JSON到DTO的自定义映射器也不会改变什么。我想要做的是:

  • 通过id从数据库中提取用户
  • 更新JSON中存在的所有字段
  • 将更新后的用户保存在数据库中

如果这是正确的做法,我将很高兴知道如何做到这一点。否则,我愿意接受任何解决方案。

英文:

So let's say i have a UserDTO.

@Getter
@Setter
public User
{
  private int id;
  private String name;
  private Integer salary;
  private Integer department_id
}

I want to update info about my user partially, via patch request, send id of a person (requiered), and other data, that can be sent partially.

Let's say i have a (2, "Dave", 10, null) user in my db. I'm sending a request, body of request is a simple json:
{"id":2, "salary":20}
And i want to use that info to update the part of the info about the person, not him entirely. The problem is - deserializer puts null's in field that are missing, in other words, he treats my json as if it was:
{"id":2, "salary":20, "name":null, "department_id":null}
and i guess, it is not what i wanna do. A straight mapping could not get the job done, since he treats MISSING fields as EMPTY fields, which are not the same things, like an empty box, and abscence of it. That makes me think that putting body of a request in a UserDTO is not a good decision.

So even a custom mapper from json to DTO won't change a thing. What i want to do is to:

  • Extract the user from db by id
  • Update all the fields that are present in json
  • Save the updated user in db

If it is correct - i would be happy to know how to do such a thing.
Otherwise - i am open to any kind of solutions.

答案1

得分: 1

你可能想查看 https://datatracker.ietf.org/doc/html/rfc6902 中的规范 RCF6902,该规范提供了一种不同的 PATCH 请求方法。

基本上,您将发送一个操作列表,以应用于指定的资源,而不是资源本身。例如,如果您需要更新用户的名称,而不是发送:

{
   "name": "New Name"
}

您将发送:

[
  { "op": "replace", "path": "name", "value": "New Name" }
]

这是强大的,因为它具有以下特点:

  • 灵活
  • 可扩展
  • 可以进行集中处理

我个人在构建 REST 微服务时更喜欢这种方法,而且我从未后悔这个决定。

编辑:我还建议阅读这篇文章 https://medium.com/@isuru89/a-better-way-to-implement-http-patch-operation-in-rest-apis-721396ac82bf

英文:

You might want to check out the specification RCF6902 in https://datatracker.ietf.org/doc/html/rfc6902 which gives a different approach to the PATCH request.

Basically you will be sending a list of operations to apply to the specified resource instead of the resource itself. For example if you had to update the name of your user instead of sending a

{
   "name": "New Name"
}

you would be sending

[
  { "op": "replace", "path": "name", "value": "New Name" }
]

It's powerful because it's:

  • flexible
  • scalable
  • can have centralized handling

I personally prefer this approach when building REST micro-services, and I never regretted this decision.

EDIT: I suggest also this article to read https://medium.com/@isuru89/a-better-way-to-implement-http-patch-operation-in-rest-apis-721396ac82bf

答案2

得分: 0

答案很简单。我完全忘记了Optional<>的存在,它可以扮演提到的包装器的角色。因此,如果根本没有传递值,那么字段本身将为null。但如果传递了值,而且该值为null,那么该字段将为Optional.empty。

英文:

The answer is simple. I totally forgot that Optional<> exists, and can take a role of a mentioned box. So if value was not passed at all, the field itself will be null. But if it was passed, and was null - the field will be Optional.empty

答案3

得分: 0

我使用反射来解决类似的问题,基本上我查找了每个字段,并在新对象中的数据不为 null 时更新字段。

        data.forEach((key, val) -> {
            if (val != null) {
                Field field = ReflectionUtils.findField(clazz, (String) key);
                if (field != null) {
                    field.setAccessible(true);
                    ReflectionUtils.setField(field, existingObj, val);
                }
            }
        });

尽管在我的情况下,我使用了Map<Object, Object> data来从请求中获取数据。

    public ResponseEntity<Object> patch(@RequestBody Map<Object, Object> data, @PathVariable Integer entityId)

希望这有所帮助。

英文:

I Used Reflection to solve similar problem, so basically I looked up every field and updated the fields if the data is not null in the newer object.

        data.forEach((key, val) -&gt; {
            if (val != null) {
                Field field = ReflectionUtils.findField(clazz, (String) key);
                if (field != null) {
                    field.setAccessible(true);
                    ReflectionUtils.setField(field, existingObj, val);
                }
            }
        });

though in my case I had used Map&lt;Object, Object&gt; data to fetch data from the request.

    public ResponseEntity&lt;Object&gt; patch(@RequestBody Map&lt;Object, Object&gt; data, @PathVariable Integer entityId)

Hope it helps.

huangapple
  • 本文由 发表于 2023年6月16日 05:51:14
  • 转载请务必保留本文链接:https://go.coder-hub.com/76485748.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定