I am making rest call like below:
REST_TEMPLATE.exchange(
external_rest_url,
HttpMethod.POST,
new HttpEntity<>(dto, getHeaders()),
Map.class)
and my headers are as below:
private HttpHeaders getHeaders() {
HttpHeaders headers = new HttpHeaders();
headers.setAccept(Collections.singletonList(MediaType.APPLICATION_JSON));
headers.setContentType(MediaType.APPLICATION_JSON);
headers.set("User-Agent","Spring's RestTemplate");
headers.set(HttpHeaders.AUTHORIZATION, "some value");
return headers;
}
when I run my code the header HttpHeaders.AUTHORIZATION is getting replaced with undefined
See request header in snapshot below from network logs:
Do anyone know why spring is behaving like this or specifically spring-web:5.0.5 jar. I tried changing the version of jar as well but result is same.
Springboot version I use is 2.0.x.
you can add an interceptor to your RestTemplate if you need to add the same headers to all requests:
public void sampleHeader(final RestTemplate restTemplate){
//Add a ClientHttpRequestInterceptor to the RestTemplate
restTemplate.getInterceptors().add(new ClientHttpRequestInterceptor(){
#Override
public ClientHttpResponse intercept(HttpRequest request, byte[] body, ClientHttpRequestExecution execution) throws IOException {
request.getHeaders().set(HttpHeaders.AUTHORIZATION, "some value");//Set the header for each request
return execution.execute(request, body);
}
});
}
Related
I created an interceptor to get the token before i do other request but i have the error "Method threw 'java.lang.StackOverflowError' exception." when i do the request.
public ClientHttpResponse intercept(HttpRequest request, byte[] body,
ClientHttpRequestExecution execution) throws IOException {
request.getHeaders().add("Authorization", "Bearer " + loginWithCredentials());
return execution.execute(request, body);
}
private String loginWithCredentials() {
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_FORM_URLENCODED);
headers.setAccept(Collections.singletonList(MediaType.APPLICATION_JSON));
MultiValueMap<String, String> map = new LinkedMultiValueMap<>();
map.add("grant_type", "password");
map.add("client_id", getClientId());
map.add("client_secret", getClientSecret());
map.add("username", getUsername());
map.add("password", getPassword());
HttpEntity<String> request = new HttpEntity<>(headers);
final ResponseEntity<String> responseEntity = getRestTemplate().exchange(
getCarrierGatewayTokenUrl(),
HttpMethod.POST,
request,
String.class);
return responseEntity.getBody();
}
At the risk of you having already figured this out:
Your interceptor runs at calls using restTemplate. Your interceptor makes a call using restTemplate.exchange. So your interceptor calls restTemplate, which runs the interceptor, which calls restTemplate... until your call stack overflows due to recursion.
A way you might avoid this is to skip executing the interceptor if you are calling the carrier gateway token url (using an if-statement), or use a different restTemplate instance without the interceptor.
I have rest template config to use restTemplate for calling 3rd Party Service API. That 3rd Party Service API needs only Basic Auth from security. So in general it looks like this
My local uri i.e. localhost:8082/api/caller -> restTemplate.getForEntity(exact 3rd party service API URL). The issue is that on restTemplate.getForEntity step, my headers are empty, but it's not a problem, I can set it using interceptor, like this
#Configuration
public class RestTemplateConfig {
#NonNull
public ClientHttpResponse intercept(HttpRequest request, byte[] body, ClientHttpRequestExecution execution) throws IOException {
HttpHeaders headers = request.getHeaders();
headers.setContentType(MediaType.APPLICATION_JSON);
headers.set("Authorization", "Basic c3VHaWxzNFlBaExZNEg6NWtwQTJuV1AAAA3YXVOd1FWWXVkTEdNjoRrQks0MUVjY1hNa3VRYUdSdE1VMDdyWUtpclNycDIzcGtASktuRQ==");
return execution.execute(request, body);
}
#Bean
public RestTemplate restTemplate() {
final RestTemplate restTemplate = new RestTemplate();
final MappingJackson2HttpMessageConverter converter = new MappingJackson2HttpMessageConverter();
converter.setObjectMapper(new ObjectMapper());
restTemplate.getMessageConverters().add(converter);
restTemplate.getInterceptors().add(this::intercept);
return restTemplate;
}
}
As you see, I've hardcoded value Authorization, but I need to have to get it retrieved automatically for every request. How to make it available for restTemplate too?
In my spring boot Application i have a scheduler which calls an API to generate token which expires in 15 min. Time of scheduler is also 15 min. please find below sample:
public class TokenGeneration {
private static String token = null;
#Scheduled(15 minutes)
public String fetchToken() {
// api call which return token
HttpEntity<model> response = restTemplate.exchange(uri, POST, entity, model.class);
token = response.getBody().getAccessToken();
}
}
I stored token value in static variable from a non static method so that i can use this token variable wherever i want to use token value. is this right approach ? if not plz let me know how i can achieve this.
Do i need to make TokenGeneration class singleton so that only one instance of this class is made throught application?
Also i want to create an interceptor or filter in which i can set Authorization headers and token value so that each request will populate authorization header automatically, i don't want to set authorization header in each request like this :
HttpHeaders headers = new HttpHeaders();
headers.set(CpsConstant.AUTHORIZATION, CpsConstant.BEARER + token);
So i tried with this custom interceptor :
public class RestTemplateInterceptor implements ClientHttpRequestInterceptor{
#Override
public ClientHttpResponse intercept(HttpRequest request, byte[] body, ClientHttpRequestExecution execution) throws IOException {
String token = TokenGeneration.token;
request.getHeaders().add("Authorization", "Bearer " + token);
return execution.execute(request, body);
}
will add this interceptor in restTemplate in config file.
So is this right approach for both token generation as well as setting headers for each request or any improvements need to be done in this approach ?
Me thinking of calling token generation method in interceptor in case of token is null like :
if(token == null){
//call token generation fetchToken method
}
It is the right approach
Spring default scope is always singleton if not specified
It is ok to use interceptor, but what if you want to call a API without a token?
Best approach to use two separate methods to send request with token and without token using a separate class
#Component
public class RestClient {
#Autowired
RestTemplate restTemplate;
public HttpHeaders getRequestHeaderBearer() {
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_JSON);
headers.add(HeaderParameters.AUTHORIZATION, HeaderParameters.BEARER +
TokenGeneration.token);
return headers;
}
public HttpHeaders getRequestHeader() {
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_JSON);
return headers;
}
public <T> ResponseEntity<T> restExchangeBearer(String url, HttpMethod httpMethod,
Class<T> classObj) {
return restTemplate.exchange(url, httpMethod,
new HttpEntity<>("parameters", this.getRequestHeaderBearer()), classObj);
}
public <T> ResponseEntity<T> restExchange(String url, HttpMethod httpMethod,
Class<T> classObj) {
return restTemplate.exchange(url, httpMethod,
new HttpEntity<>("parameters", this.getRequestHeader()), classObj);
}
}
I have a Spring Boot application using jax-rs with resteasy (3.0.24). I'm trying to get the HttpHeaders for a request as such:
#DELETE
#Path("/myendpoint")
public Response myMethod(#Context HttpHeaders headers, #Context HttpServletRequest request) {
// headers is always null
}
The headers param is always null even though I'm making the request with multiple headers. As an alternative, I'm extracting them via the HttpServletRequest.getHeaderNames(), but I'd really like know why headers is not populated.
Found the (embarrassing, although I deflect the blame to the author:)) error. #Context HttpHeaders headers was using Spring's implementation and not that from jax-rs.
You gotta get the headers using the #Context then check if the one one you want is there.
#Path("/users")
public class UserService {
#GET
#Path("/get")
public Response addUser(#Context HttpHeaders headers) {
String userAgent = headers.getRequestHeader("user-agent").get(0);
return Response.status(200)
.entity("addUser is called, userAgent : " + userAgent)
.build();
}
}
This controller works fine
#Controller
public class FileUploadController {
....
#PostMapping("/convert")
public void fileUpload(#RequestParam("file") MultipartFile file,
RedirectAttributes redirectAttributes, HttpServletResponse response) {
Now i want to call this controller from another spring project via RestTemplate. I tried many things, but noting works. Here is my last code:
#Controller
public class FileController {
....
#PostMapping("/convert")
public void fileUpload(#RequestParam("file") MultipartFile file, RedirectAttributes redirectAttributes,
HttpServletResponse response) throws Exception {
ArrayList<HttpMessageConverter<?>> converters = new ArrayList<HttpMessageConverter<?>>(
Arrays.asList(new FormHttpMessageConverter(),new MappingJackson2HttpMessageConverter(), new ResourceHttpMessageConverter()));
RestTemplate template = restTemplate();
template.setMessageConverters(converters);
HttpHeaders header = new HttpHeaders();
header.setContentType(MediaType.MULTIPART_FORM_DATA);
MultiValueMap<String, Object> multipartRequest = new LinkedMultiValueMap<>();
multipartRequest.add("file", file);
HttpEntity<MultiValueMap<String, Object>> requestEntity = new HttpEntity<>(multipartRequest, header);
template.postForObject("http://localhost:8080/convert", requestEntity, String.class);
}
I if call FileUploadController (via postman) it works. If if call FileController
i get this Exception
"exception":
"org.springframework.http.converter.HttpMessageNotWritableException",
"message": "Could not write request: no suitable HttpMessageConverter found for request type [org.springframework.web.multipart.support.StandardMultipartHttpServletRequest$StandardMultipartFile]",
"path": "/convert"
Take a look at the answer here, it should be exactly what you are looking for: Attempting to test rest service with multipart file
The issue there is about posting a multi-part file to a rest service using a RestTemplate.
Basically, what you have to do is to simulate a file upload. You can try something like this:
MultiValueMap<String, Object> parameters = new LinkedMultiValueMap<String, Object>();
parameters.add("file", new FileSystemResource("file.jpg"));
HttpHeaders headers = new HttpHeaders();
headers.set("Content-Type", "multipart/form-data");
headers.set("Accept", "text/plain");
String result = restTemplate.postForObject(
"http://host:port/path",
new HttpEntity<MultiValueMap<String, Object>>(parameters, headers),
String.class);