Spring跨系统转发HTTP请求的简易实现(支持下载文件二进制流)
背景
有一个系统是Web端,作为Gateway服务,背后有许多微服务,可以经过HTTP转发过去,同时这个Web系统作为权限认证,因此可以修改一些Header,放一些用户信息给到后端系统。
需求
就是将HttpServletRequest 翻译成一个RestTemplate请求
使用RestTemplate转发所有请求
public static ResponseEntity<byte[]> dispatchHttpRequest(HttpServletRequest request, String url,
Map<String, String[]> params, HttpHeaders headerParams,
String service, boolean isFile) {
HttpHeaders requestHeaders = new HttpHeaders();
HttpMethod httpMethod = HttpMethod.resolve(request.getMethod());
//复制原始HttpHeaders
Enumeration<?> headerNames = request.getHeaderNames();
while (headerNames.hasMoreElements()) {
String key = (String) headerNames.nextElement();
String value = request.getHeader(key);
requestHeaders.set(key, value);
}
requestHeaders.setAll(headerParams.toSingleValueMap());
if (httpMethod.equals(HttpMethod.GET)) {
requestHeaders.set(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_FORM_URLENCODED_VALUE);
}
//读取RequestBody, 似乎读取不到 Form参数。
String dataFromRequest = getJsonDataFromRequest(request);
UriComponentsBuilder builder = UriComponentsBuilder.fromHttpUrl(url).query(request.getQueryString());
UriComponents build;
try {
params.forEach(builder::queryParam);
build = builder.build(true);
}catch (Exception e) {
throw new BaseException(-1, "参数不合法:" + e.getMessage());
}
Optional<List<String>> contentTypes = Optional.ofNullable(requestHeaders.get(HttpHeaders.CONTENT_TYPE));
//修改Content-Type
if (contentTypes.isPresent()
&& !contentTypes.get().contains(MediaType.APPLICATION_FORM_URLENCODED_VALUE + ";charset=UTF-8")
&& !contentTypes.get().contains(MediaType.APPLICATION_FORM_URLENCODED_VALUE)
&& !contentTypes.get().contains(MediaType.APPLICATION_JSON_UTF8_VALUE)
&& !contentTypes.get().contains(MediaType.APPLICATION_JSON_VALUE)
) {
requestHeaders.setContentType(MediaType.APPLICATION_JSON_UTF8);
}
//设置默认值为JSON。
if (!httpMethod.equals(HttpMethod.GET) && !contentTypes.isPresent()) {
requestHeaders.setContentType(MediaType.APPLICATION_JSON_UTF8);
}
logger.info("data from request: {}", dataFromRequest);
HttpEntity<?> entity = new HttpEntity<>(dataFromRequest, requestHeaders);
//处理 APPLICATION_FORM_URLENCODED形式编码 的参数
if (!httpMethod.equals(HttpMethod.GET)
&& contentTypes.isPresent() && (
contentTypes.get().contains(MediaType.APPLICATION_FORM_URLENCODED_VALUE)
|| contentTypes.get().contains(MediaType.APPLICATION_FORM_URLENCODED_VALUE + ";charset=UTF-8"))
&& requestHeaders.getContentLength() > 0
) {
MultiValueMap<String, String> parameters = new LinkedMultiValueMap<>();
Enumeration parameterNames = request.getParameterNames();
while (parameterNames.hasMoreElements()) {
String o = (String)parameterNames.nextElement();
if (!build.getQueryParams().containsKey(o)) {
parameters.add(o, request.getParameter(o));
}
}
logger.info("parameterMap {}", parameters);
if (StringUtils.isBlank(dataFromRequest)) {
entity = new HttpEntity<>(parameters, requestHeaders);
}
}
logger.info("Content-Type: {}, Content-Length: {}", requestHeaders.getContentType(), requestHeaders.getContentLength());
logger.debug("after url: {} entity: {}", build.toUriString(), JSON.toJSONString(entity));
ResponseEntity<byte[]> response;
try {
response = restTemplate.exchange(build.toUri(), httpMethod, entity, byte[].class);
}catch (HttpClientErrorException httpClientException) {
if (httpClientException.getStatusCode().value() == 404) {
throw new BaseException(-1, service + "可能已经离线或重启中,请稍后再访问,因为您访问的地址出现404错误");
}
if (isFile && httpClientException.getStatusCode().value() >= 400) {
return ResponseEntity.status(HttpStatus.NOT_FOUND).body(httpClientException.getResponseBodyAsByteArray());
}
throw httpClientException;
}
return response;
}
private static String getJsonDataFromRequest(HttpServletRequest request) {
BufferedReader br = null;
StringBuilder sb = new StringBuilder();
try {
br = new BufferedReader(new InputStreamReader(request.getInputStream(), "utf-8"));
String temp;
while ((temp = br.readLine()) != null) {
sb.append(temp);
}
br.close();
} catch (IOException e) {
e.printStackTrace();
}
return sb.toString();
}