如何在Spring Boot服务应用程序中的睡觉服务调用之间按原样传递请求参数? [英] How to pass request parameters as-is between REST service calls in a Spring Boot services application?
问题描述
我们正在进行架构重构,将单一的J2EEEJB应用程序转换为Spring服务。为了做到这一点,我通过在应用程序的域的节点上破坏应用程序来创建服务。目前我有三个,每个都通过睡觉调用另一个服务。
在这个项目中,我们的最终目的是将应用转化为微服务,但是由于云基础设施不清楚,而且很可能不可能实现,所以我们决定这样做,并认为既然服务使用睡觉,将来转化会很容易。
我们的方法有意义吗?我的问题源于此。
我使用标头参数UserName from Postman向UserService发送请求。
GET http://localhost:8087/users/userId?userName=12345
UserService调用另一个服务,该服务调用另一个服务。服务间睡觉通话顺序为:
UserService ---REST--> CustomerService ---REST--> AlarmService
由于我现在正在做的是像这样携带公共请求参数的工作,所以我需要在每个睡觉请求的方法中设置公共头部参数,从传入请求到传出请求:
@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();
}
}
我已经尝试过这些配置和拦截器文件,但我只能将它们用于日志记录,不能使用它们来传输头参数。可能是因为每个服务都有它们。而且,这个拦截器只适用于首先使用RestTemplate发送请求的UserService。调用的服务和来自Postman的第一个请求不能与其一起工作,因为它们不像UserService那样打印任何日志消息。
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=============");
}
}
如何通过使用某种拦截器或其他机制在单个位置管理用户名等公共标头信息的传递?
推荐答案
在HeaderRequestInterceptor的Intercept方法中,您可以通过以下方式访问当前的Http请求及其标头(本例中为userID):
@Override
public ClientHttpResponse intercept(HttpRequest request..
...
HttpServletRequest httpServletRequest = ((ServletRequestAttributes) RequestContextHolder.currentRequestAttributes()).getRequest();
String userId = httpServletRequest.getHeader("userId");
request.getHeaders().set("userId", userId);
这篇关于如何在Spring Boot服务应用程序中的睡觉服务调用之间按原样传递请求参数?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!