Spring boot rest service, как заставить его маршалировать ссылки как свойства?
Первый вопрос вот так будь нежнее:)
У меня есть проект JPA, который я хочу представить как REST. Я сделал это до сих пор:
Моя сущность:
@Entity
public class SignUpSheet {
@Id
@GeneratedValue
private Long id;
@Column
private String name;
@Column
private String description;
@Column
@Temporal(TemporalType.TIMESTAMP)
private Date dateTime;
@ManyToOne
private User parent;
@OneToMany
private List<Volunteer> volunteers;
//getter and setters
}
Все хорошо, я звоню добавил spring-boot-starter-data-rest к моему pom, и теперь я получаю услугу. Вот JSON, я вернусь.
http://localhost:8080/api-0.1.0/signUpSheets/1
{
"name": "Auction",
"description": "My First Sign Up Sheet",
"dateTime": "2015-04-22T03:47:12.000+0000",
"volunteers": [
{
"role": "Bringing stuff",
"comments": "I have comments!"
}
],
"endpoint": "/signUpSheets",
"_links": {
"self": {
"href": "http://localhost:8080/api-0.1.0/signUpSheets/1"
},
"parent": {
"href": "http://localhost:8080/api-0.1.0/signUpSheets/1/parent"
},
"user": {
"href": "http://localhost:8080/api-0.1.0/signUpSheets/1/user"
}
}
}
Супер! Почти то, что я ожидал. Теперь я вызываю свой сервис, используя Spring RestTemplate, и вот где я застрял. Когда он маршалирует обратно в объект SignUpSheet, он вытягивает большую часть объекта, но поле идентификатора является нулевым (что имеет смысл, поскольку в Json нет поля идентификатора, только собственная ссылка), и все объекты OneToMany и ManyToOne являются нуль (я полагаю, по той же причине).
У меня вопрос: как мне сказать Spring Hateoas добавить идентификатор в json или указать Джексону, как добавить его в поле идентификатора? Кроме того, как я могу получить ссылки? Если бы мне не пришлось маршалировать обратно в сущность JPA, а вместо этого создать еще один POJO для SignUpSheet (что-то, чего я хотел бы избежать в целях дублирования, но о котором можно поговорить, если это необходимо / желательно по какой-то причине, по которой я скучаю). Я добавил Jackson2HalModule в мой ObjectMapper, но это, кажется, не имеет значения, есть ли он там или нет.
@Bean
@Primary
public ObjectMapper objectMapper() {
ObjectMapper o = new ObjectMapper();
o.registerModule(new Jackson2HalModule());
return o;
}
Заранее спасибо за помощь!
================================================== =====
Решение:
Первый шаг, прочитайте инструкцию:)
Итак, я узнал, что мне нужно расширить ResourceSupport для моих недавно созданных DTO. Сделано и сделано. Но я не получал никаких ссылок назад! Похоже, мне нужно было добавить Jackson2HalModule к объекту отображения на RestTemplate следующим образом:
ObjectMapper o = new ObjectMapper();
o.registerModule(new Jackson2HalModule());
MappingJackson2HttpMessageConverter c = new MappingJackson2HttpMessageConverter();
c.setObjectMapper(o);
restTemplate.getMessageConverters().add(0, c);
Поэтому я полагаю, что буду расширять RestTemplate и @Component, и я подхожу для любого ресурса HATEOAS.
2 ответа
Я не думаю, что вы должны пытаться десериализовать JSON обратно в вашу сущность JPA. Сущность JPA очень тесно связана с базой данных вашего приложения и должна рассматриваться как деталь реализации сервера. Вместо этого я бы порекомендовал сопоставить типу, который специально смоделирован по REST API, а не по структуре вашей базы данных и использованию вами JPA.
Вы используете Spring Data REST, который сильно охватывает гипермедиа. Это означает, что клиенты должны использовать URI для идентификации ресурсов и ссылок для навигации между ними. Например, на стороне клиента лист регистрации уже имеет идентификатор; это href
из self
ссылка в ответе. Следовательно, нет необходимости предоставлять идентификатор из вашей сущности JPA. В самом деле, при этом будут раскрыты детали реализации вашего приложения, о которых клиенты не должны знать.
Вместо того, чтобы пытаться заполнить все свойства в ответе, вместо этого Spring Data REST предоставляет ссылки. Например, чтобы получить доступ к родителю регистрационного листа, вы должны извлечь href
из parent
ссылку из ответа и выполнить GET
запрос на URI.
Вам нужно продлить RepositoryRestConfigurerAdapter
чтобы сказать вам нужен Id для экспорта, например:
public class RepositoryConfig extends RepositoryRestConfigurerAdapter {
@Override
public void configureRepositoryRestConfiguration(RepositoryRestConfiguration config) {
config.exposeIdsFor(SignUpSheet.class);
}
}