使用 Spring 返回 Rest API 中的实体

jos*_*fin 5 java rest spring json

在 Spring 中为 Web 应用程序创建 Restful api 非常简单。假设我们有一个电影实体,其中包含名称、年份、类型列表和演员列表。为了以 json 格式返回所有电影的列表,我们只需在某个控制器中创建一个方法,该方法将查询数据库并返回一个列表作为 ResponseEntity 的主体。Spring 会神奇地序列化它,一切都很好:)

但是,如果我在某些情况下希望序列化一部电影中的演员列表,而不是其他电影中的演员列表,该怎么办?在其他情况下,除了电影类的字段之外,我还需要为列表中的每部电影添加一些其他属性,哪些值是动态生成的?

我当前的解决方案是在某些字段上使用 @JsonIgnore 或创建一个 MovieResponse 类,其中包含 Movie 类中的字段和所需的其他字段,并每次从 Movie 转换为 MovieResponse 类。

有一个更好的方法吗?

jmo*_*253 2

JSONIgnore 注释的要点是告诉 DispatcherServlet(或 Spring 中处理渲染响应的任何组件)忽略某些字段(如果这些字段为 null 或以其他方式省略)。

这可以为您在某些情况下向客户端公开哪些数据提供一定的灵活性。

JSONIgnore 的缺点:

然而,我最近在自己的项目中遇到了使用此注释的一些缺点。这主要适用于 PUT 方法以及控制器将数据序列化到的对象与用于在数据库中存储该数据的对象相同的情况。

PUT 方法意味着您要在服务器上创建新集合,或者用要更新的新集合替换服务器上的集合。

替换服务器上的集合的示例:

想象一下,您正在向服务器发出 PUT 请求,并且 RequestBody 包含序列化的 Movie 实体,但该 Movie 实体不包含演员,因为您省略了他们!随后,您将实现一项新功能,允许用户编辑和更正电影描述中的拼写错误,然后使用 PUT 将电影实体发送回服务器,并更新数据库。

但是,我们可以这么说——因为自从您将 JSONIgnore 添加到对象以来已经很久了——您已经忘记了某些字段是可选的。在客户端,您忘记包含演员集合,现在您的用户不小心用演员 B、C 和 D 覆盖了电影 A,而电影 A 却没有任何演员!

为什么 JSONIgnore 选择加入?

按理说,强迫您选择不将某些字段设置为必填字段的目的正是为了避免此类数据完整性问题。在不使用 JSONIgnore 的情况下,您可以保证您的数据永远不会被部分数据替换,除非您自己显式设置该数据。使用 JSONIgnore,您可以消除这些保护措施。

话虽如此,JSONIgnore 非常有价值,我自己以完全相同的方式使用它来减少发送到客户端的有效负载的大小。然而,我开始重新考虑这一策略,转而选择在单独的层中使用 POJO 类将数据发送到前端的策略,而不是与数据库交互的策略。

可能更好的设置?:

根据我处理这个特定问题的经验,理想的设置是对实体对象使用构造函数注入而不是设置器。强迫自己必须在实例化时传入每个参数,以便您的实体永远不会被部分填充。如果您尝试部分填充它们,编译器会阻止您做一些您可能会后悔的事情。

为了将数据发送到客户端,您可能希望省略某些数据片段,可以使用单独的、断开连接的实体 POJO,或使用 org.json 中的 JSONObject。

当从客户端向服务器发送数据时,您的前端实体对象从模型数据库层接收部分或全部数据,因为您并不真正关心前端是否获取部分数据。但是,当将数据存储在数据存储中时,您将首先从数据存储中获取已存储的对象,更新其属性,然后将其存储回数据存储中。换句话说,如果您缺少参与者,也没关系,因为您从数据存储更新的对象已经将参与者分配给了它的属性。因此,您只需替换您明确想要替换的字段。

虽然此设置会产生更多的维护开销和复杂性,但您将获得强大的优势:Java 编译器将为您提供支持!它不会让您甚至倒霉的同事在代码中执行任何可能危及数据存储中数据的操作。如果您尝试在模型层中动态创建实体,您将被迫使用构造函数,并被迫提供所有数据。如果您没有所有数据并且无法实例化该对象,那么您需要传递空值(这应该向您发出危险信号)或首先从数据存储中获取该数据。