【问题标题】:Spring HATEOAS and Entity DTO - problem with ModelAssemblerSpring HATEOAS 和实体 DTO - ModelAssembler 的问题
【发布时间】:2020-10-09 23:21:49
【问题描述】:

我正在开发 Spring HATEOAS REST api。 现在我遇到了隐藏Employee类的一些字段(我想隐藏其中一些)。 据我了解,必须使用 DTO(数据传输对象)。所以我创建了只包含我需要的这个字段的新类(EmployeeDTO)。我正在使用ModelMapper 映射适当的字段。

所以现在,我遇到了数据类型的问题。我是否必须将控制器中的所有返回值从 Employee 更改为 EmployeeDTO?然后更改 ModelAssembler 中的所有内容?

或者也许 DTO 类应该extends RepresentationModel? (这也迫使它在实体中扩展)。 期待您的帮助,所有这些看起来有点混乱(更改所有返回类型),我觉得应该有更聪明的解决方案。

我现在就是这样:

EmployeeController 中的方法:

@GetMapping(value = "/{id}", produces = "application/hal+json")
    public EntityModel<Employee> getEmployeeById(@PathVariable Long id) {
        Optional<Employee> employee = employeeService.findById(id);
        return employeeModelAssembler.toModel(employee.get());
}

@GetMapping(value = "", produces = "application/hal+json")
    public CollectionModel<EntityModel<Employee>> getAllEmployees() {
        List<Employee> employeesList = EmployeeService.findall();
        return employeeModelAssembler.toCollectionModel(EmployeesList);
}

EmployeeModelAssembler(在控制器中自动装配):

public class EmployeeModelAssembler implements RepresentationModelAssembler<Employee, EntityModel<Employee>> {

    @Override
    public EntityModel<Employee> toModel(Employee employee) {
        EntityModel<Employee> employeeEntityModel = EntityModel.of(employee);

        Link selfLink = linkTo(methodOn(EmployeeController.class).getEmployeeById(employee.getId())).withSelfRel();
        employeeEntityModel.add(selfLink);

        return employeeEntityModel;
    }

    @Override
    public CollectionModel<EntityModel<Employee>> toCollectionModel(Iterable<? extends Employee> entities) {

        List<Employee> employeesList = (List<Employee>) entities;
        List<EntityModel<Employee>> employeeEML = employeesList.stream().map(this::toModel).collect(Collectors.toList());

        Link selfLink = linkTo(methodOn(EmployeeController.class).getAllEmployees()).withSelfRel();

        return CollectionModel.of(employeeEML, selfLink);
    }
}

编辑

这是我在另一个控制器中访问这个 hal+json 响应的方式。目标是获取 EmployeeDTO 对象列表:

String uri = "http://localhost:8080/api/employees";

Traverson traverson = new Traverson(URI.create(uri), MediaTypes.HAL_JSON);
Traverson.TraversalBuilder tb = traverson.follow("href");

ParameterizedTypeReference<CollectionModel<EmployeeDTO>> typeReference = new ParameterizedTypeReference<CollectionModel<EmployeeDTO>>() {};
CollectionModel<EmployeeDTO> resEmployees = tb.toObject(typeReference);
Collection<EmployeeDTO> employees = resEmployees.getContent();

ArrayList<EmployeesDTO> employeesList = new ArrayList<>(employees);

for(EmployeesDTO x : employeesList) {
    System.out.println(x);
    System.out.println(x.getLinks());
}

【问题讨论】:

    标签: spring spring-boot dto spring-rest spring-hateoas


    【解决方案1】:

    如果您想将 API 公开的资源与 JPA 实体分离,这是一个好主意,您必须更新控制器以处理适当的类型。

    请看下面的例子:

    首先,假设您有以下 DTO:

    public class EmployeeDTO extends RepresentationModel<EmployeeDTO> {
          private long id;
          private String surname;
          private String departmentId;
    
          // getters and setters
    }
    

    请注意我没有将您的 JPA 实体(员工)与 DTO 混合。

    现在应该更新您的控制器以返回适当的类型。

    @GetMapping(value = "/{id}", produces = "application/hal+json")
    public EmployeeDTO getEmployeeById(@PathVariable long id) {
           Optional<Employee> employee = employeeService.findById(id);
           return employeeModelAssembler.toModel(employee.get());
    }
    
    @GetMapping(value = "", produces = "application/hal+json")
    public CollectionModel<EmployeeDTO> getAllEmployees() {
        List<Employee> employeesList = employeeService.findAll();
        return employeeModelAssembler.toCollectionModel(employeesList);
    }
    

    还有一个 ModelAssembler 的例子

    @Component
    public class EmployeeModelAssembler implements RepresentationModelAssembler<Employee, EmployeeDTO> {
    
        @Override
        public EmployeeDTO toModel(Employee employee) {
            ModelMapper modelMapper = new ModelMapper();
            EmployeeDTO employeeDto = modelMapper.map(employee, EmployeeDTO.class);
    
            Link selfLink = linkTo(methodOn(EmployeeController.class).getEmployeeById(employee.getId())).withSelfRel();
            employeeDto.add(selfLink);
    
            return employeeDto;
        }
    
        @Override
        public CollectionModel<EmployeeDTO> toCollectionModel(Iterable<? extends Employee> employeesList) {
           ModelMapper modelMapper = new ModelMapper();
           List<EmployeeDTO> employeeDTOS = new ArrayList<>();
    
           for (Employee employee : employeesList){
               EmployeeDTO employeeDto = modelMapper.map(employee, EmployeeDTO.class);
               employeeDto.add(linkTo(methodOn(EmployeeController.class)
                                    .getEmployeeById(employeeDto.getId())).withSelfRel());
               employeeDTOS.add(employeeDto);
            }
    
            return new CollectionModel<>(employeeDTOS);
        }
    }
    

    假设我有一个员工端点,我对其进行了测试并得到了以下响应

    获得员工/1

    {
    "id": 1,
    "surname": "teste",
    "departmentId": "1",
    "_links": {
        "self": {
            "href": "http://localhost:8080/employees/1"
        }
    }
    }
    

    找员工

    {
        "_embedded": {
            "employeeDtoList": [
                {
                    "id": 1,
                    "surname": "teste",
                    "departmentId": "1",
                    "_links": {
                        "self": {
                            "href": "http://localhost:8080/employees/1"
                        }
                    }
                },
                {
                    "id": 2,
                    "surname": "teste",
                    "departmentId": "2",
                    "_links": {
                        "self": {
                            "href": "http://localhost:8080/employees/2"
                        }
                    }
                }
            ]
        }
    }
    

    【讨论】:

    • 这是非常宝贵的帮助。您是否也可以帮助将这个 hal+json 解析回另一个控制器中? (可能使用 Traverson)。几乎没有这方面的信息。如果您说愿意,那么我将创建新帖子并将链接发送给您/
    • @Nickname11 可能,我今天不在线太久了,但无论如何你应该发布这个问题。如果我还在这里,我会去看看。 Traverson 似乎确实是一个不错的选择。创建时将链接发送给我!再见
    • 实际上,我想我自己设法处理了它(在这篇文章中作为编辑添加),我的解决方案很混乱,但它正在工作,但最困扰我的部分是 @987654326 @
    猜你喜欢
    • 2021-06-16
    • 1970-01-01
    • 1970-01-01
    • 2016-09-02
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多