在 Spring Boot 项目中,我尝试使用 HATEOAS 以 hal+json 格式序列化和反序列化相关对象。我正在使用
org.springframework.boot:spring-boot-starter-hateoas
来执行此操作,它在我的控制器中效果很好:
@GetMapping(path = "/{id}", produces = MediaTypes.HAL_JSON_VALUE + ";charset=UTF-8")
HttpEntity<Item> getItemById(@PathVariable Integer id) {
Item item = itemRepository.findById(id).get();
item.add(linkTo(ItemController.class).slash(item.getId()).withSelfRel());
item.add(linkTo(methodOn(CategoryController.class).getCategoryById(item.getCategory().getId()))
.withRel("category"));
return new HttpEntity<>(item);
}
Item
类是:
@Entity
public class Item extends RepresentationModel<Item> {
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
@Column(name = "item_id")
private Integer id;
@ManyToOne
@JoinColumn(name = "category_id", referencedColumnName = "category_id")
@JsonIgnore
private Category category;
private String name;
// Getters and Setters are present but not entered here in the question
}
调用 GET 方法将导致类似的结果
{
"name": "foo"
"_links": {
"self": {
"href": "http://localhost:8080/items/1"
},
"category": {
"href": "http://localhost:8080/categories/2"
}
},
"item_id": 1
}
这正是我想要的。
当我尝试为 POST 方法编写测试(旨在用于创建新的
Item
)时,现在出现了问题。这就是我的测试的样子:
@Test
void testCreate() throws Exception {
Item testItem = new Item("test");
testItem.add(Link.of("http://localhost:" + port + "/categories/2", "category"));
// (objectMapper was @Autowire'd on class level)
objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
ObjectWriter ow = objectMapper.writer().withDefaultPrettyPrinter();
String itemSerialized = ow.writeValueAsString(testItem);
mockMvc.perform(post("/items/")
.content(itemSerialized)
.contentType(MediaTypes.HAL_JSON_VALUE))
.andExpect(status().isOK());
}
此序列化无法按需要工作,因为关系在
itemSerialized
字符串中如下所示:
"links" : [{
"rel" : "category",
"href" : "http://localhost:61524/categories/2"
}]
(正确的是带有下划线的
_links
和上面给出的category
关系结构。)
据我了解,造成这种情况的原因是,我使用的
objectMapper
不知道需要序列化为json+hal格式。 (当控制器从 Item
返回 getItemById
时,默认/魔术会正确发生。)现在,确保测试中也发生正确序列化的正确方法是什么?
我读过有关使用
objectMapper.registerModule(new Jackson2HalModule());
的内容,这对我来说似乎很合理。但是我使用的时候却出现异常:
org.springframework.beans.factory.BeanCreationException:
Error creating bean with name 'org.springframework.hateoas.mediatype.hal.Jackson2HalModule$HalLinkListSerializer':
Failed to instantiate [org.springframework.hateoas.mediatype.hal.Jackson2HalModule$HalLinkListSerializer]:
No default constructor found
任何帮助将不胜感激!
在这种情况下,您必须与 spring 上下文一起运行测试,以便框架执行必须在内部完成的所有步骤,以返回正确的 HATEOAS 响应。
最好使用 TestRestTemplate,因为它是与 jayway 和 AssertJ 库一起使用的 Spring 解决方案。即使您正在进行 TDD,这也会使测试变得更容易
下面我留下一个来自Spring Academy
的例子@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
class CashCardApplicationTests {
@Autowired
TestRestTemplate restTemplate;
@Test
void shouldReturnACashCardWhenDataIsSaved() {
ResponseEntity<String> response = restTemplate.getForEntity("/cashcards/99", String.class);
assertThat(response.getStatusCode()).isEqualTo(HttpStatus.OK);
DocumentContext documentContext = JsonPath.parse(response.getBody());
Number id = documentContext.read("$.id");
assertThat(id).isEqualTo(99);
Double amount = documentContext.read("$.amount");
assertThat(amount).isEqualTo(123.45);
}
}
使用与此类似的结构,您可以轻松执行所有断言和测试