【发布时间】:2014-02-01 18:53:03
【问题描述】:
我需要通过使用RestTemplate 发送带有一些授权标头的 GET 请求来从我的服务器检索资源。
在查看docs 之后,我注意到没有一个 GET 方法接受标头作为参数,而发送标头(如接受和授权)的唯一方法是使用 exchange 方法。
由于这是一个非常基本的操作,我想知道我是否遗漏了什么,还有另一种更简单的方法吗?
【问题讨论】:
标签: java spring spring-mvc resttemplate
我需要通过使用RestTemplate 发送带有一些授权标头的 GET 请求来从我的服务器检索资源。
在查看docs 之后,我注意到没有一个 GET 方法接受标头作为参数,而发送标头(如接受和授权)的唯一方法是使用 exchange 方法。
由于这是一个非常基本的操作,我想知道我是否遗漏了什么,还有另一种更简单的方法吗?
【问题讨论】:
标签: java spring spring-mvc resttemplate
你没有错过任何东西。 RestTemplate#exchange(..) 是用于设置请求标头的适当方法。
Here's an example(使用 POST,但只需将其更改为 GET 并使用您想要的实体)。
请注意,使用 GET,您的请求实体不必包含任何内容(除非您的 API 需要它,但这会违反 HTTP 规范)。可以是空字符串。
【讨论】:
您可以将postForObject 与HttpEntity 一起使用。它看起来像这样:
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_JSON);
headers.set("Authorization", "Bearer "+accessToken);
HttpEntity<String> entity = new HttpEntity<String>(requestJson,headers);
String result = restTemplate.postForObject(url, entity, String.class);
在 GET 请求中,您通常不会发送正文(这是允许的,但它没有任何用途)。添加标头而不以不同方式连接 RestTemplate 的方法是直接使用 exchange 或 execute 方法。 get 速记不支持 header 修改。
乍一看,不对称有点奇怪,也许这将在未来的 Spring 版本中得到修复。
【讨论】:
restTemplate.postForEntity(url, entity, String.class) 也可以正常工作。
getForObject有这样的签名。
这是一个超级简单的示例,包含基本身份验证、标头和异常处理...
private HttpHeaders createHttpHeaders(String user, String password)
{
String notEncoded = user + ":" + password;
String encodedAuth = "Basic " + Base64.getEncoder().encodeToString(notEncoded.getBytes());
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_JSON);
headers.add("Authorization", encodedAuth);
return headers;
}
private void doYourThing()
{
String theUrl = "http://blah.blah.com:8080/rest/api/blah";
RestTemplate restTemplate = new RestTemplate();
try {
HttpHeaders headers = createHttpHeaders("fred","1234");
HttpEntity<String> entity = new HttpEntity<String>("parameters", headers);
ResponseEntity<String> response = restTemplate.exchange(theUrl, HttpMethod.GET, entity, String.class);
System.out.println("Result - status ("+ response.getStatusCode() + ") has body: " + response.hasBody());
}
catch (Exception eek) {
System.out.println("** Exception: "+ eek.getMessage());
}
}
【讨论】:
所有这些答案似乎都不完整和/或杂乱无章。查看 RestTemplate 接口,它确实看起来像是打算将ClientHttpRequestFactory 注入其中,然后该 requestFactory 将用于创建请求,包括对标头、正文和请求参数的任何自定义。
你要么需要一个通用的ClientHttpRequestFactory 来注入一个共享的RestTemplate,要么你需要通过new RestTemplate(myHttpRequestFactory) 获取一个新的模板实例。
不幸的是,即使您只想设置一个 Authorization 标头,创建这样一个工厂看起来也有些不简单,考虑到这可能是一个常见的要求,这非常令人沮丧,但至少它允许在以下情况下轻松使用,例如,您的 Authorization 标头可以从 Spring-Security Authorization 对象中包含的数据创建,然后您可以创建一个工厂,通过执行 SecurityContextHolder.getContext().getAuthorization() 然后填充标头,在每个请求上设置传出 AuthorizationHeader,使用 null酌情检查。现在,使用该 RestTemplate 进行的所有出站休息调用都将具有正确的 Authorization 标头。
如果不更加强调 HttpClientFactory 机制,为常见情况(例如向请求添加单个标头)提供易于重载的基类,RestTemplate 的大多数不错的便捷方法最终都浪费了时间,因为它们只能很少使用。
我希望看到像这样简单的东西可用
@Configuration
public class MyConfig {
@Bean
public RestTemplate getRestTemplate() {
return new RestTemplate(new AbstractHeaderRewritingHttpClientFactory() {
@Override
public HttpHeaders modifyHeaders(HttpHeaders headers) {
headers.addHeader("Authorization", computeAuthString());
return headers;
}
public String computeAuthString() {
// do something better than this, but you get the idea
return SecurityContextHolder.getContext().getAuthorization().getCredential();
}
});
}
}
目前,可用的 ClientHttpRequestFactory 的接口比这更难交互。更好的是现有工厂实现的抽象包装器,这使它们看起来像 AbstractHeaderRewritingRequestFactory 之类的更简单的对象,目的是仅替换那一项功能。目前,它们的用途非常广泛,因此即使编写这些包装器也是一项复杂的研究。
【讨论】:
如今,以下内容就足够了:
HttpHeaders headers = new HttpHeaders();
headers.setBearerAuth(accessToken);
restTemplate.exchange(RequestEntity.get(new URI(url)).headers(headers).build(), returnType);
【讨论】:
一个简单的解决方案是在 RestTemplate 的 bean 配置中配置所有调用所需的静态 http 标头:
@Configuration
public class RestTemplateConfig {
@Bean
public RestTemplate getRestTemplate(@Value("${did-service.bearer-token}") String bearerToken) {
RestTemplate restTemplate = new RestTemplate();
restTemplate.getInterceptors().add((request, body, clientHttpRequestExecution) -> {
HttpHeaders headers = request.getHeaders();
if (!headers.containsKey("Authorization")) {
String token = bearerToken.toLowerCase().startsWith("bearer") ? bearerToken : "Bearer " + bearerToken;
request.getHeaders().add("Authorization", token);
}
return clientHttpRequestExecution.execute(request, body);
});
return restTemplate;
}
}
【讨论】: