本文介绍了使用Spring在Rest API中返回实体的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

Creating a restful api for a web application in Spring is pretty easy.Let's say we have a Movie entity, with a name, year, list of genres and list of actors. In order to return a list of all movies in json format, we just create a method in some controller that will query a database and return a list as a body of ResponseEntity. Spring will magically serialize it, and all works great :)

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

But, what if I, in some case, want that list of actors in a movie to be serialized, and not in other? And in some other case, alongside the fields of the movie class, I need to add some other properties, for each movie in the list, which values are dynamically generated?

我当前的解决方案是在某些字段上使用@JsonIgnore或创建具有Movie类和所需其他字段之类的MovieResponse类,并每次将Movie转换为MovieResponse类.

My current solution is to use @JsonIgnore on some fields or to create a MovieResponse class with fields like in Movie class and additional fields that are needed, and to convert from Movie to MovieResponse class each time.

有更好的方法吗?

推荐答案

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

The point of the JSONIgnore annotation is to tell the DispatcherServlet (or whatever component in Spring handles rendering the response) to ignore certain fields if those fields are null or otherwise omitted.

在某些情况下,这可以为您提供一些灵活性,使其可以向客户端公开哪些数据.

This can provide you with some flexibility in terms of what data you expose to the client in certain cases.

JSONIgnore的缺点:

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

However, there are some downsides to using this annotation that I've recently encountered in my own projects. This applies mainly to the PUT method and cases where the object that your controller serializes data to is the same object that is used to store that data in the database.

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

The PUT method implies that you're either creating a new collection on the server or are replacing a collection on the server with the new collection you're updating.

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

想象一下,您正在向服务器发出PUT请求,并且RequestBody包含序列化的Movie实体,但是此Movie实体不包含任何actor,因为您已将它们省略了!稍后,您将实现一项新功能,该功能使您的用户可以编辑和更正电影"说明中的拼写错误,并使用PUT将电影"实体发送​​回服务器,并更新数据库.

Imagine that you're making a PUT request to your server, and the RequestBody contains a serialized Movie entity, but this Movie entity contains no actors because you've omitted them! Later on down the road, you implement a new feature that allows your users to edit and correct spelling errors in the Movie description, and you use PUT to send the Movie entity back to the server, and you update the database.

但是,可以这么说-因为自从向对象添加JSONIgnore以来已经很久了-您已经忘记了某些字段是可选的.在客户端,您忘记了包括演员的集合,现在您的用户意外地用演员B,C和D覆盖了电影A,而影片A没有任何演员!

But, let's say that -- because it's been so long since you added JSONIgnore to your objects -- you've forgotten that certain fields are optional. In the client side, you forget to include the collection of actors, and now your user accidentally overwrites Movie A with actors B, C, and D, with Movie A with no actors whatsoever!

为什么选择JSONIgnore?

有理由认为,强迫您选择退出某些必填字段的意图恰恰是为了避免此类数据完整性问题.在不使用JSONIgnore的世界中,您可以保证除非您自行明确设置数据,否则绝不能将数据替换为部分数据.使用JSONIgnore,您可以删除这些保护措施.

It stands to reason that the intention behind forcing you to opt-out of making certain fields required is precisely so that these types of data integrity issues are avoided. In a world where you're not using JSONIgnore, you guarantee that your data can never be replaced with partial data unless you explicitly set that data yourself. With JSONIgnore, you remove these safeguards.

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

With that said, JSONIgnore is very valuable, and I use it myself in precisely the same manner to reduce the size of the payload sent to the client. However, I'm beginning to rethink this strategy and instead opt for one where I use POJO classes in a separate layer for sending data to the frontend than what I use to interact with the database.

可能更好的设置吗?:

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

The ideal setup -- from my experience dealing with this particular problem -- is to use Constructor injection for your Entity objects instead of setters. Force yourself to have to pass in every parameter at instantiation time so that your entities are never partially filled. If you try to partially fill them, the compiler stops you from doing something you may regret.

要向客户端发送数据(您可能希望省略某些数据),可以使用单独的,断开连接的实体POJO,或使用org.json中的JSONObject.

For sending data to the client side, where you may want to omit certain pieces of data, you could use a separate, disconnected entity POJO, or use a JSONObject from org.json.

从客户端向服务器发送数据时,您的前端实体对象部分或全部接收来自模型数据库层的数据,因为您实际上并不关心前端是否获得部分数据.但是,当将数据存储在数据存储区中时,您将首先从数据存储区中获取已存储的对象,更新其属性,然后再将其存储回数据存储区中.换句话说,如果您缺少参与者,则没关系,因为您要从数据存储区更新的对象已经为其属性分配了参与者. 因此,您仅替换您明确打算替换的字段.

When sending data from the client to the server, your frontend entity objects receive the data from the model database layer, partially or full, since you don't really care if the frontend gets partial data. But then when storing the data in the datastore, you would first fetch the already-stored object from the datastore, update its properties, and then store it back in the datastore. In other words, if you were missing the actors, it wouldn't matter because the object you're updating from the datastore already has the actors assigned to it's properties. Thus, you only replace the fields that you explicitly intend to replace.

尽管此设置会有更多的维护开销和复杂性,但您将获得强大的优势:Java编译器会帮您解决问题!它不会让您,甚至一个不幸的同事也不会这样做.代码中可能损害数据存储区中数据的所有内容.如果尝试在模型层中动态创建实体,则将被迫使用构造函数,并被迫提供所有数据.如果您没有所有数据并且无法实例化该对象,那么您要么需要传递空值(应该向您发出红色标记),要么首先从数据存储中获取该数据.

While there would be more maintenance overhead and complexity to this setup, you would gain a powerful advantage: The Java compiler would have your back! It won't let you or even a hapless colleague do anything in the code that might compromise the data in the datastore. If you attempt to create an entity on the fly in your model layer, you'll be forced to use the constructor, and forced to provide all of the data. If you don't have all of the data and cannot instantiate the object, then you'll either need to pass empty values (which should signal a red flag to you) or fetch that data from the datastore first.

这篇关于使用Spring在Rest API中返回实体的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!

05-27 04:05
查看更多