Reputation: 148
We are doing an architectural refactoring to convert a monolithic J2EE EJB application to Spring services. In order to do that I'm creating services by breaking the application against the joints of its domain. Currently, I have three of them and each calls another service via Rest.
In this project our ultimate purpose is transforming the application to microservices, but since cloud infrastructure isn't clear and probably won't be possible, we decided to make it this way and thought that since services using Rest, it will be easy to make the transform in future.
Does our approach makes sense? My question stems from this.
I send a request to UserService with a header parameter, userName from Postman.
GET http://localhost:8087/users/userId?userName=12345
UserService calls another service which calls another. Rest call order between services is this:
UserService ---REST--> CustomerService ---REST--> AlarmService
Since I'm doing the work of carrying the common request parameters like this right now, I need to set common header parameters in every method that making Rest requests by taking them from incoming request to outgoing request:
@RequestMapping(value="/users/userId", method = RequestMethod.GET)
public ResponseEntity<Long> getUserId(@RequestHeader("userName") String userName) {
...
HttpHeaders headers = new HttpHeaders();
headers.setAccept(Collections.singletonList
(MediaType.APPLICATION_JSON));
headers.set("userName", userName);
HttpEntity<String> entity = new HttpEntity<>("parameters", headers);
HttpEntity<Long> response =
restTemplate.exchange(CUSTOMER_REST_SERVICE_URI,
HttpMethod.GET, entity, Long.class);
...
}
UserService:
package com.xxx.userservice.impl;
import org.apache.log4j.Logger;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.*;
import org.springframework.web.bind.annotation.RequestHeader;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestTemplate;
import java.util.Collections;
import java.util.Map;
@RestController
public class UserController extends AbstractService{
Logger logger = Logger.getLogger(UserController.class.getName());
@Autowired
private RestTemplate restTemplate;
private final String CUSTOMER_REST_SERVICE_HOST = "http://localhost:8085";
private final String CUSTOMER_REST_SERVICE_URI = CUSTOMER_REST_SERVICE_HOST + "/customers/userId";
@RequestMapping(value="/users/userId", method = RequestMethod.GET)
public ResponseEntity<Long> getUserId(@RequestHeader("userName") String userName) {
logger.info(""user service is calling customer service..."");
try {
//do the internal customer service logic
//call other service.
HttpHeaders headers = new HttpHeaders();
headers.setAccept(Collections.singletonList
(MediaType.APPLICATION_JSON));
headers.set("userName", userName);
HttpEntity<String> entity = new HttpEntity<>("parameters", headers);
HttpEntity<Long> response =
restTemplate.exchange(CUSTOMER_REST_SERVICE_URI,
HttpMethod.GET, entity, Long.class);
return ResponseEntity.ok(response.getBody());
} catch (Exception e) {
logger.error("user service could not call customer service: ", e);
throw new RuntimeException(e);
}
finally {
logger.info("customer service called...");
}
}
}
CustomerService:
package com.xxxx.customerservice.impl;
import java.rmi.RemoteException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import com.xxx.interf.CustomerService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestHeader;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class CustomerController extends AbstractService{
private final String ALARM_REST_SERVICE_HOST = "http://localhost:8086";
private final String ALARM_REST_SERVICE_URI = ALARM_REST_SERVICE_HOST + "/alarms/maxAlarmCount";
@Autowired
private CustomerService customerService;
@Autowired
private RestTemplate restTemplate;
...
@GetMapping(path="/customers/userId", produces = "application/json")
public long getUserId(@RequestHeader(value="Accept") String acceptType) throws RemoteException {
//customer service internal logic.
customerService.getUserId();
//customer service calling alarm service.
return restTemplate.getForObject(ALARM_REST_SERVICE_URI, Long.class);
}
}
AlarmService:
package com.xxx.alarmservice.impl;
import com.xxx.interf.AlarmService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class PriceAlarmController extends AbstractService{
@Autowired
private AlarmService priceAlarmService;
@RequestMapping("/alarms/maxAlarmCount")
public long getMaxAlarmsPerUser() {
// alarm service internal logic.
return priceAlarmService.getMaxAlarmsPerUser();
}
}
I have tried these config and interceptor files but i can use them just for logging and can't transfer header parameters by using them. Probably because each service has them. And also, this interceptor only works in UserService which first uses RestTemplate to send request. Called service and first request which is coming from Postman doesn't work with it because they doesn't print any log message like UserService does.
CommonModule:
package com.xxx.common.config;
import com.xxx.common.util.HeaderRequestInterceptor;
import org.apache.cxf.common.util.CollectionUtils;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.client.ClientHttpRequestInterceptor;
import org.springframework.web.client.RestTemplate;
import java.util.ArrayList;
import java.util.List;
@Configuration
public class RestTemplateConfig {
@Bean
public RestTemplate restTemplate() {
RestTemplate restTemplate = new RestTemplate();
List<ClientHttpRequestInterceptor> interceptors
= restTemplate.getInterceptors();
if (CollectionUtils.isEmpty(interceptors)) {
interceptors = new ArrayList<>();
}
interceptors.add(new HeaderRequestInterceptor());
restTemplate.setInterceptors(interceptors);
return restTemplate;
}
}
ClientHttpRequestInterceptor:
package com.xxx.common.util;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.http.HttpRequest;
import org.springframework.http.MediaType;
import org.springframework.http.client.ClientHttpRequestExecution;
import org.springframework.http.client.ClientHttpRequestInterceptor;
import org.springframework.http.client.ClientHttpResponse;
import org.springframework.util.StreamUtils;
import java.io.IOException;
import java.nio.charset.Charset;
public class HeaderRequestInterceptor implements ClientHttpRequestInterceptor {
private final Logger log = LoggerFactory.getLogger(this.getClass());
@Override
public ClientHttpResponse intercept(
HttpRequest request,
byte[] body,
ClientHttpRequestExecution execution) throws IOException
{
log.info("HeaderRequestInterceptor....");
logRequest(request, body);
request.getHeaders().set("Accept", MediaType.APPLICATION_JSON_VALUE);
ClientHttpResponse response = execution.execute(request, body);
logResponse(response);
return response;
}
private void logRequest(HttpRequest request, byte[] body) throws IOException
{
log.info("==========request begin=======================");
}
private void logResponse(ClientHttpResponse response) throws IOException
{
log.info("==========response begin=============");
}
}
How can I manage the passing of common header information like userName by using some kind of interceptors or other mechanism in single place?
Upvotes: 2
Views: 9661
Reputation: 1739
In your HeaderRequestInterceptor's intercept method, you can access the current http request and its headers (userId in your case) in the following way:
@Override
public ClientHttpResponse intercept(HttpRequest request..
...
HttpServletRequest httpServletRequest = ((ServletRequestAttributes) RequestContextHolder.currentRequestAttributes()).getRequest();
String userId = httpServletRequest.getHeader("userId");
request.getHeaders().set("userId", userId);
Upvotes: 3