我正试图从一个REST服务中检索一个资源集合。我请求这个资源集合的代码如下。
public CollectionModel<EntityModel<Doctor>> getDoctors() {
log.info("Request to get all doctor resources");
ParameterizedTypeReference<CollectionModel<EntityModel<Doctor>>> doctorTypeReference =
new ParameterizedTypeReference<CollectionModel<EntityModel<Doctor>>>() {};
CollectionModel<EntityModel<Doctor>> doctorResources = traverson
.follow(doctorsLink)
.toObject(doctorTypeReference);
log.info("The received resources are: " + doctorResources);
return doctorResources;
}
其中 Doctor
就是一个POJO。
@Data // Lombok annotation for setters, getter, equals, ...
@Relation(value = "doctor", collectionRelation = "doctors")
public class Doctor {
private Long id;
private String firstName;
private String lastNames;
private String email;
private String status;
@JsonIgnore
private Map<String, String> links;
}
和TRAVERSON被注入 @Autowired
注释。
@Autowired
private Traverson traverson;
其配置如下。
@Configuration
@Slf4j
public class RestConfiguration {
@Value("${api.uri}")
private String restUri;
@Value("${auth.rest.username}")
private String username;
@Value("${auth.rest.password}")
private String password;
@Value("${ssl.trust-store}")
private Resource trustStore;
@Value("${ssl.trust-store.password}")
private String trustStorePassword;
@Bean
public RestTemplateBuilder restTemplateBuilder() {
return new RestTemplateBuilder(restTemplate -> {
// Configure SSL to accept self-signed certificates
SSLContext sslContext = null;
try {
sslContext = SSLContexts.custom()
.loadTrustMaterial(null, new TrustSelfSignedStrategy()).build();
} catch (KeyManagementException | NoSuchAlgorithmException | KeyStoreException e) {
log.error("Unexpected exception while creating SSLContext: " + e);
}
SSLConnectionSocketFactory socketFactory =
new SSLConnectionSocketFactory(sslContext, NoopHostnameVerifier.INSTANCE);
CloseableHttpClient httpClient = HttpClients.custom()
.setSSLSocketFactory(socketFactory)
.build();
HttpComponentsClientHttpRequestFactory requestFactory =
new HttpComponentsClientHttpRequestFactory();
requestFactory.setHttpClient(httpClient);
restTemplate.setRequestFactory(requestFactory);
// Configure basic authentication
restTemplate.getInterceptors().add(
new BasicAuthenticationInterceptor(username, password));
});
}
@Bean
public Traverson traverson() {
Traverson traverson = new Traverson(URI.create(restUri), MediaTypes.HAL_JSON);
// Configure the RestTemplates used by the Traverson
traverson.setRestOperations(restTemplateBuilder().build());
return traverson;
}
}
我只是用了一个自签的SSL证书和基本的HTTP认证。
这样说来,记录的信息在经过 getDoctors
方法被调用是。
The received resources are: Resources { content: [], links: [] }
我试过用 RestTemplate
直接使用。
public CollectionModel<EntityModel<Doctor>> getDoctors() {
log.info("Request to get all doctor resources");
ParameterizedTypeReference<CollectionModel<EntityModel<Doctor>>> doctorTypeReference =
new ParameterizedTypeReference<CollectionModel<EntityModel<Doctor>>>() {};
// builder is the @Autowired RestTemplateBuilder
CollectionModel<EntityModel<Doctor>> doctorResources = builder.build()
.exchange("https://127.0.0.1:8080/guardians/api/doctors",
HttpMethod.GET, null, doctorTypeReference).getBody();
log.info("The received resources are: " + doctorResources);
ResponseEntity<Object> resp = builder.build()
.exchange("https://127.0.0.1:8080/guardians/api/doctors",
HttpMethod.GET, null, Object.class);
log.info("The resources using rest template are: " + resp);
return doctorResources;
}
使用... ParameterizedTypeReference
作为响应类型,接收到的集合仍然是空的。然而,使用 Object.class
作为响应类型, ResponseEntity
体实际上包含了预期的信息。
The resources using rest template are: <200,{_embedded={doctors=[{id=1, firstName=1, lastNames=1, [email protected], status=AVAILABLE, absence=null, _links={self={href=https://127.0.0.1:8080/guardians/api/doctors/1}, doctors={href=https://127.0.0.1:8080/guardians/api/doctors/{?email}, templated=true}, shiftConfig={href=https://127.0.0.1:8080/guardians/api/doctors/shift-configs/1}, updateDoctor={href=https://127.0.0.1:8080/guardians/api/doctors/1}}}, {id=2, firstName=2, lastNames=2, [email protected], status=AVAILABLE, absence=null, _links={self={href=https://127.0.0.1:8080/guardians/api/doctors/2}, doctors={href=https://127.0.0.1:8080/guardians/api/doctors/{?email}, templated=true}, shiftConfig={href=https://127.0.0.1:8080/guardians/api/doctors/shift-configs/2}, updateDoctor={href=https://127.0.0.1:8080/guardians/api/doctors/2}}}, {id=3, firstName=3, lastNames=3, [email protected], status=AVAILABLE, absence=null, _links={self={href=https://127.0.0.1:8080/guardians/api/doctors/3}, doctors={href=https://127.0.0.1:8080/guardians/api/doctors/{?email}, templated=true}, shiftConfig={href=https://127.0.0.1:8080/guardians/api/doctors/shift-configs/3}, updateDoctor={href=https://127.0.0.1:8080/guardians/api/doctors/3}}}, {id=4, firstName=4, lastNames=4, [email protected], status=AVAILABLE, absence=null, _links={self={href=https://127.0.0.1:8080/guardians/api/doctors/4}, doctors={href=https://127.0.0.1:8080/guardians/api/doctors/{?email}, templated=true}, shiftConfig={href=https://127.0.0.1:8080/guardians/api/doctors/shift-configs/4}, updateDoctor={href=https://127.0.0.1:8080/guardians/api/doctors/4}}},..., {id=22, firstName=22, lastNames=22, [email protected], status=AVAILABLE, absence=null, _links={self={href=https://127.0.0.1:8080/guardians/api/doctors/22}, doctors={href=https://127.0.0.1:8080/guardians/api/doctors/{?email}, templated=true}, shiftConfig={href=https://127.0.0.1:8080/guardians/api/doctors/shift-configs/22}, updateDoctor={href=https://127.0.0.1:8080/guardians/api/doctors/22}}}]}, _links={self={href=https://127.0.0.1:8080/guardians/api/doctors/{?email}, templated=true}, newDoctor={href=https://127.0.0.1:8080/guardians/api/doctors/?startDate={startDate}, templated=true}, root={href=https://127.0.0.1:8080/guardians/api/}}},[Set-Cookie:"JSESSIONID=6F797BDA2827ACD4590708F5E354122A; Path=/guardians/api; Secure; HttpOnly", X-Content-Type-Options:"nosniff", X-XSS-Protection:"1; mode=block", Cache-Control:"no-cache, no-store, max-age=0, must-revalidate", Pragma:"no-cache", Expires:"0", Strict-Transport-Security:"max-age=31536000 ; includeSubDomains", X-Frame-Options:"DENY", Content-Type:"application/json", Transfer-Encoding:"chunked", Date:"Tue, 09 Jun 2020 11:21:01 GMT", Keep-Alive:"timeout=60", Connection:"keep-alive"]>
那么,如果我只是请求一个单一的资源,
public EntityModel<Doctor> getDoctor(Long doctorId) {
log.info("Request to get doctor " + doctorId);
ParameterizedTypeReference<EntityModel<Doctor>> doctorTypeReference =
new ParameterizedTypeReference<EntityModel<Doctor>>() {};
EntityModel<Doctor> doctorEntity;
try {
doctorEntity = traverson
.follow(Hop.rel(doctorLink).withParameter("doctorId", doctorId))
.toObject(doctorTypeReference);
} catch (NotFound e) {
log.info("The doctor was not found");
throw e;
}
log.info("The received resource is: " + doctorEntity);
return doctorEntity;
}
内容会被反序列化,但链接却没有。
The received resource is: Resource { content: Doctor(id=1, firstName=1, lastNames=1, [email protected], status=AVAILABLE, links=null), links: [] }
我正在使用Spring框架为一个Web应用程序服务。这个应用程序使用Spring-Hateoas消耗REST服务。
我发现了问题所在:我没有配置消息转换器的参数。RestTemplate
来使用HAL。
为了解决这个问题,我在配置的最后添加了以下内容 RestTemplate
:
restTemplate.setMessageConverters(
Traverson.getDefaultMessageConverters(MediaTypes.HAL_JSON));
现在... Bean
该文件提供了 RestTemplateBuilder
看起来像这样。
@Bean
public RestTemplateBuilder restTemplateBuilder() {
return new RestTemplateBuilder(restTemplate -> {
// Configure SSL to accept self-signed certificates
SSLContext sslContext = null;
try {
sslContext = SSLContexts.custom()
.loadTrustMaterial(null, new TrustSelfSignedStrategy()).build();
} catch (KeyManagementException | NoSuchAlgorithmException | KeyStoreException e) {
log.error("Unexpected exception while creating SSLContext: " + e);
}
SSLConnectionSocketFactory socketFactory =
new SSLConnectionSocketFactory(sslContext, NoopHostnameVerifier.INSTANCE);
CloseableHttpClient httpClient = HttpClients.custom()
.setSSLSocketFactory(socketFactory)
.build();
HttpComponentsClientHttpRequestFactory requestFactory =
new HttpComponentsClientHttpRequestFactory();
requestFactory.setHttpClient(httpClient);
restTemplate.setRequestFactory(requestFactory);
// Configure basic authentication
restTemplate.getInterceptors().add(
new BasicAuthenticationInterceptor(username, password));
// Configure the restTemplate to use the default HAL message converter
restTemplate.setMessageConverters(
Traverson.getDefaultMessageConverters(MediaTypes.HAL_JSON));
});
}