如何使控制器端点在 java spring 中获取两个不同的对象?
How to make controller endpoint to get two different objects in java spring?
我有一个使用 java 和 spring 构建的服务器。
我想要做的是我的具有相同端点的控制器将获得两个不同的对象。
这是我的意思的一个例子:
我知道我能做到:
public class Option1{
private String name;
...
//getter and setter
}
public class Option2{
private Long id;
...
//getter and setter
}
@Controller
public class Controller{
@RequestMapping(value = "service/getData/option1", method = RequestMethod.POST)
@ResponseBody
public String searchProv(@ResponseBody Option1 data1){
return "option1"
}
@RequestMapping(value = "service/getData/option2", method = RequestMethod.POST)
@ResponseBody
public String searchProv(@ResponseBody Option2 data2){
return "option2"
}
}
但我想知道是否可以将不同的 json 对象传递到同一端点并执行此操作:
@Controller
public class Controller{
@RequestMapping(value = "service/getData", method = RequestMethod.POST)
@ResponseBody
public ResponseEntity<Any> getData(@ResponseBody Option1And2 data){
if(data instanceof Option1){
return return ResponseEntity<Any>(data.name,HttpStatus.OK)
}
if(data instanceof Option2){
return ResponseEntity<Any>(data.id,HttpStatus.OK)
}
return ResponseEntity<Any>("ok",HttpStatus.OK)
}
这样 'Option1And2' 是通用对象,可以是选项 1 或选项 2。
我尝试将 'Option1And2' 替换为 'Any' 但效果不佳,因为我得到了键和值列表
似乎你希望程序本身在你这样做之前确定选项的类型is.But,你确定这两个对象之间有什么区别吗?
首先,Option1And2 实际上是什么?如果 Option1And2 包含 Option1 和 Option2 的所有字段但它不是它们的子类,那么 Option1And2 可能如下所示:
@Data
public class Option1And2{
private String name;
private Long id;
}
- 如果您有其他限制,例如“其中一个并且只有一个有
为null",则可以通过这个规则判断。
- 如果你没有任何其他限制,那么也许你可以添加一个新的
字段作为标志。
其实那些代码风格不是recommend.If这两个函数有不同的职责,那么最好不要把它们混在一起。你会明白我的意思的当你必须重构这些代码时。
如果这两个功能确实有很多共同点,也许您最好重构服务逻辑,而不是通过创建新参数 Option1And2 粗略地组合两个服务。
顺便问一下,你到底想做什么?为什么要把这两个对象合并为一个?
这是使用继承和 Java 泛型的好时机。值得注意的是,如果您的控制器有任何依赖项,例如 @Service
或 @Repository
,那么这些也必须是通用的。
您可能有一个通用控制器:
abstract class GenericController<T> {
public abstract GenericService<T> getService();
@GetMapping
public ResponseEntity<Iterable<T>> findAll() {
return ResponseEntity.ok(getService().findAll());
}
@PostMapping
public ResponseEntity<T> save(T entity) {
return ResponseEntity.ok(getService().save(entity));
}
// @DeleteMapping, @PutMapping
// These mappings will automatically be inherited by
// the child class. So in the case of findAll(), the API
// will have a GET mapping on /category as well as a GET
// mapping on /product. So, by defining and annotating the
// CRUD operations in the parent class, they will automatically
// become available in all child classes.
}
@Controller
@RequestMapping("/category")
class CategoryContr extends GenericController<Category> {
@Autowired CategoryServ serv;
@Override
public GenericService<Category> getService() {
return serv;
}
}
@Controller
@RequestMapping("/product")
class ProductContr extends GenericController<Product> {
@Autowired ProductServ serv;
@Override
public GenericService<Product> getService() {
return serv;
}
}
然后您必须具有依赖项的抽象版本。服务:
abstract class GenericService<T> {
public abstract GenericRepository<T> getRepository();
public Iterable<T> findAll() {
return getRepository().findAll();
}
public T save(T entity) {
return getRepository().save(entity);
}
}
@Service
class CategoryServ extends GenericService<Category> {
@Autowired CategoryRepo repo;
@Override
public GenericRepository<Category> getRepository() {
return repo;
}
}
@Service
class ProductServ extends GenericService<Product> {
@Autowired ProductRepo repo;
@Override
public GenericRepository<Product> getRepository() {
return repo;
}
}
然后,服务也有它们的依赖关系——存储库:
@NoRepositoryBean
interface GenericRepository<T> extends JpaRepository<T, Long> {
}
@Repository
interface CategoryRepo extends GenericRepository<Category> {
}
@Repository
interface ProductRepo extends GenericRepository<Product> {
}
这是我的第一个方法。它工作得很好。但是,这确实在每个服务的业务逻辑和通用服务之间建立了强耦合。这同样适用于通用控制器及其子 classes。您当然可以始终覆盖特定的 CRUD 操作。但是,您必须小心执行此操作,因为您可能会产生意外行为。还值得注意的是,继承自 class 具有使用 @RequestMapping
注释的方法的 es 会自动公开所有注释的方法。这可能是不希望的。例如,我们可能不想要类别的删除选项,但我们想要产品的删除选项。为了解决这个问题,我们可以简单地在父 class 中定义它,而不是在父 class 中注释方法,并使用添加的 @RequestMapping
注释覆盖所需的 CRUD 操作,然后调用超级 class 方法。
另一种方法是using annotations.
你应该使用 JsonNode 对象。
对于您的示例,您应该这样做:
@Controller
public class Controller{
@RequestMapping(value = "service/getData", method = RequestMethod.POST)
@ResponseBody
public ResponseEntity<Any> getData(@RequestBody JsonNode jsonNode){
ObjectMapper obj = new ObjectMapper();
if(jsonNode.has("name"){
Option1 result= obj.convertValue(jsonNode,Option1.class)
return ResponseEntity<Any>(result.name,HttpStatus.OK)
}
else {
Option2 result= obj.convertValue(jsonNode,Option2.class)
return ResponseEntity<Any>(result.id,HttpStatus.OK)
}
return ResponseEntity<Any>("ok",HttpStatus.OK)
}
您应该从此处导入的 JsonNode 和 ObjectMapper:
import com.fasterxml.jackson.databind.ObjectMapper
import com.fasterxml.jackson.databind.JsonNode;
这篇 link 应该可以帮助您更好地了解 JsonNode 并为您提供更多详细信息。
而这个 link 应该可以帮助您将 convertValue 从 JsonNode 转换为 java object(POJO)。
我有一个使用 java 和 spring 构建的服务器。
我想要做的是我的具有相同端点的控制器将获得两个不同的对象。
这是我的意思的一个例子:
我知道我能做到:
public class Option1{
private String name;
...
//getter and setter
}
public class Option2{
private Long id;
...
//getter and setter
}
@Controller
public class Controller{
@RequestMapping(value = "service/getData/option1", method = RequestMethod.POST)
@ResponseBody
public String searchProv(@ResponseBody Option1 data1){
return "option1"
}
@RequestMapping(value = "service/getData/option2", method = RequestMethod.POST)
@ResponseBody
public String searchProv(@ResponseBody Option2 data2){
return "option2"
}
}
但我想知道是否可以将不同的 json 对象传递到同一端点并执行此操作:
@Controller
public class Controller{
@RequestMapping(value = "service/getData", method = RequestMethod.POST)
@ResponseBody
public ResponseEntity<Any> getData(@ResponseBody Option1And2 data){
if(data instanceof Option1){
return return ResponseEntity<Any>(data.name,HttpStatus.OK)
}
if(data instanceof Option2){
return ResponseEntity<Any>(data.id,HttpStatus.OK)
}
return ResponseEntity<Any>("ok",HttpStatus.OK)
}
这样 'Option1And2' 是通用对象,可以是选项 1 或选项 2。
我尝试将 'Option1And2' 替换为 'Any' 但效果不佳,因为我得到了键和值列表
似乎你希望程序本身在你这样做之前确定选项的类型is.But,你确定这两个对象之间有什么区别吗?
首先,Option1And2 实际上是什么?如果 Option1And2 包含 Option1 和 Option2 的所有字段但它不是它们的子类,那么 Option1And2 可能如下所示:
@Data
public class Option1And2{
private String name;
private Long id;
}
- 如果您有其他限制,例如“其中一个并且只有一个有 为null",则可以通过这个规则判断。
- 如果你没有任何其他限制,那么也许你可以添加一个新的 字段作为标志。
其实那些代码风格不是recommend.If这两个函数有不同的职责,那么最好不要把它们混在一起。你会明白我的意思的当你必须重构这些代码时。
如果这两个功能确实有很多共同点,也许您最好重构服务逻辑,而不是通过创建新参数 Option1And2 粗略地组合两个服务。
顺便问一下,你到底想做什么?为什么要把这两个对象合并为一个?
这是使用继承和 Java 泛型的好时机。值得注意的是,如果您的控制器有任何依赖项,例如 @Service
或 @Repository
,那么这些也必须是通用的。
您可能有一个通用控制器:
abstract class GenericController<T> {
public abstract GenericService<T> getService();
@GetMapping
public ResponseEntity<Iterable<T>> findAll() {
return ResponseEntity.ok(getService().findAll());
}
@PostMapping
public ResponseEntity<T> save(T entity) {
return ResponseEntity.ok(getService().save(entity));
}
// @DeleteMapping, @PutMapping
// These mappings will automatically be inherited by
// the child class. So in the case of findAll(), the API
// will have a GET mapping on /category as well as a GET
// mapping on /product. So, by defining and annotating the
// CRUD operations in the parent class, they will automatically
// become available in all child classes.
}
@Controller
@RequestMapping("/category")
class CategoryContr extends GenericController<Category> {
@Autowired CategoryServ serv;
@Override
public GenericService<Category> getService() {
return serv;
}
}
@Controller
@RequestMapping("/product")
class ProductContr extends GenericController<Product> {
@Autowired ProductServ serv;
@Override
public GenericService<Product> getService() {
return serv;
}
}
然后您必须具有依赖项的抽象版本。服务:
abstract class GenericService<T> {
public abstract GenericRepository<T> getRepository();
public Iterable<T> findAll() {
return getRepository().findAll();
}
public T save(T entity) {
return getRepository().save(entity);
}
}
@Service
class CategoryServ extends GenericService<Category> {
@Autowired CategoryRepo repo;
@Override
public GenericRepository<Category> getRepository() {
return repo;
}
}
@Service
class ProductServ extends GenericService<Product> {
@Autowired ProductRepo repo;
@Override
public GenericRepository<Product> getRepository() {
return repo;
}
}
然后,服务也有它们的依赖关系——存储库:
@NoRepositoryBean
interface GenericRepository<T> extends JpaRepository<T, Long> {
}
@Repository
interface CategoryRepo extends GenericRepository<Category> {
}
@Repository
interface ProductRepo extends GenericRepository<Product> {
}
这是我的第一个方法。它工作得很好。但是,这确实在每个服务的业务逻辑和通用服务之间建立了强耦合。这同样适用于通用控制器及其子 classes。您当然可以始终覆盖特定的 CRUD 操作。但是,您必须小心执行此操作,因为您可能会产生意外行为。还值得注意的是,继承自 class 具有使用 @RequestMapping
注释的方法的 es 会自动公开所有注释的方法。这可能是不希望的。例如,我们可能不想要类别的删除选项,但我们想要产品的删除选项。为了解决这个问题,我们可以简单地在父 class 中定义它,而不是在父 class 中注释方法,并使用添加的 @RequestMapping
注释覆盖所需的 CRUD 操作,然后调用超级 class 方法。
另一种方法是using annotations.
你应该使用 JsonNode 对象。
对于您的示例,您应该这样做:
@Controller
public class Controller{
@RequestMapping(value = "service/getData", method = RequestMethod.POST)
@ResponseBody
public ResponseEntity<Any> getData(@RequestBody JsonNode jsonNode){
ObjectMapper obj = new ObjectMapper();
if(jsonNode.has("name"){
Option1 result= obj.convertValue(jsonNode,Option1.class)
return ResponseEntity<Any>(result.name,HttpStatus.OK)
}
else {
Option2 result= obj.convertValue(jsonNode,Option2.class)
return ResponseEntity<Any>(result.id,HttpStatus.OK)
}
return ResponseEntity<Any>("ok",HttpStatus.OK)
}
您应该从此处导入的 JsonNode 和 ObjectMapper:
import com.fasterxml.jackson.databind.ObjectMapper
import com.fasterxml.jackson.databind.JsonNode;
这篇 link 应该可以帮助您更好地了解 JsonNode 并为您提供更多详细信息。
而这个 link 应该可以帮助您将 convertValue 从 JsonNode 转换为 java object(POJO)。