我正在开发Spring HATEOAS REST API。 现在,我需要隐藏Employee类的某些字段(我想隐藏其中的一些字段)。
据我了解,必须使用DTO(数据传输对象)。因此,我仅使用此字段创建了新类(EmployeeDTO)。我正在使用ModelMapper
来映射适当的字段。
所以现在,我对数据类型有疑问。我是否必须将Controller中的所有返回值从Employee更改为EmployeeDTO?然后更改ModelAssembler中的所有内容?
也许DTO类应该extends RepresentationModel
? (这也迫使它在Entity中扩展)。
期待您的帮助,所有这些看起来都有些混乱(更改所有返回类型),我觉得应该有更明智的解决方案。
这就是我现在正在做的事情:
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(在Controller中自动连线):
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());
}
答案 0 :(得分: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"
}
}
}
]
}
}