场景:开发一个简易的API网关,该网关要与现有的权限系统集成,完成验证后创建token,并根据路由配置完成请求的转发,期间将使用RestTemplate来完成转发操作,并结合Ribbon完成负载均衡。
这里主要说明怎么使用RestTemplate进行转发,其余的部分不予赘述。
首先自定义一个Filter并完成注册
public class RouteFilter implements Filter {
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
throws IOException, ServletException {
}
}
在JavaConfig文件中完成注册
@Bean
public FilterRegistrationBean<RouteFilter> routeFilter(RestTemplate restTemplate, Routes remotes,@Autowired(required = false)DiscoveryClient discoveryClient) {
FilterRegistrationBean<RouteFilter> filterRegistration = new FilterRegistrationBean<RouteFilter>();
filterRegistration.setFilter(new RouteFilter(restTemplate, remotes,discoveryClient));
filterRegistration.addUrlPatterns("/*");
filterRegistration.setOrder(20);
return filterRegistration;
}
完整代码
public class RouteFilter implements Filter {
private final long MAX_SIZE = 10 * 1024 * 1024 * 1024;// 设置上传文件最大为 10G
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
throws IOException, ServletException {
if (ServletFileUpload.isMultipartContent(req)) {
this.uploadDispatch(req, res, toUrl);
} else {
this.doDispatch(req, res, toUrl);
}
}
/**
* 请求的转发
*【该种方式在对上传文件进行转发时,如果文件很大其内存将会是一个瓶颈,所以可以考虑下面种方式(uploadDispatch)】
* */
private void doDispatch(HttpServletRequest req, HttpServletResponse res, String toUrl) throws ServletException {
RequestEntity<byte[]> requestEntity = null;
try {
requestEntity = this.createRequestEntity(req, toUrl);
} catch (URISyntaxException | IOException e) {
throw new ServletException(e);
}
ResponseEntity<byte[]> responseEntity = null;
try {
responseEntity = restTemplate.exchange(requestEntity, byte[].class);
} catch (Exception e) {
throw new ServletException(e);
}
// 开始执行跳转
HttpHeaders httpHeaders = responseEntity.getHeaders();
for (Map.Entry<String, List<String>> entry : httpHeaders.entrySet()) {
String headerName = entry.getKey();
List<String> headerValues = entry.getValue();
for (String headerValue : headerValues) {
res.addHeader(headerName, headerValue);
}
}
if (responseEntity.hasBody()) {
ServletOutputStream outputStream = null;
try {
outputStream = res.getOutputStream();
outputStream.write(responseEntity.getBody());
outputStream.flush();
} catch (IOException e) {
throw new ServletException(e);
}
}
}
public void uploadDispatch(ServletRequest request, ServletResponse response,String toUrl)
throws IOException, ServletException {
DiskFileItemFactory factory = new DiskFileItemFactory();
// 设置内存缓冲区,超过后写入临时文件
factory.setSizeThreshold(4096);
// 设置上传到服务器上文件的临时存放目录 -- 非常重要,防止存放到系统盘造成系统盘空间不足
factory.setRepository(new File("./uploadFileTemp"));
ServletFileUpload fileUpload = new ServletFileUpload(factory);
fileUpload.setHeaderEncoding("utf-8");
// 设置单个文件的最大上传值
fileUpload.setSizeMax(MAX_SIZE); // 文件上传上限10G
List<FileItem> fileItemList = null;
try {
fileItemList = fileUpload.parseRequest(req);
} catch (FileUploadException e) {
log.error("上传文件解析错误,{}", e.getMessage());
throw new ServletException(e);
}
/*
* 注意,在SpringMVC环境中,需要配置spring.servlet.multipart.enabled=false
* 来去掉SpringMVC对上传操作的解析,否则这里得到的上传文件个数为0
* */
if (fileItemList == null || fileItemList.size() == 0) {
throw new ServletException("没有文件");
}
List<Object> fileList = new ArrayList<>();
for (final FileItem fileItem : fileItemList) {
log.info(">>>file name:{}", fileItem.getName());
ByteArrayResource byteArr = new ByteArrayResource(fileItem.get()) {
@Override
public String getFilename() throws IllegalStateException {
return fileItem.getName();
}
};
fileList.add(byteArr);
}
// 进行转发
MultiValueMap<String, Object> parts = new LinkedMultiValueMap<>();
if(fileList.size() == 1) {
parts.add("file", fileList.get(0));
}else {
parts.add("file", fileList);
}
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.MULTIPART_FORM_DATA);
HttpEntity<MultiValueMap<String, Object>> mutiReq = new HttpEntity<>(parts, headers);
responseEntity = restTemplate.exchange(toUrl, HttpMethod.POST, mutiReq, byte[].class,
new HashMap<String, Object>());
if (responseEntity.hasBody()) {
try {
ServletOutputStream outputStream = res.getOutputStream();
outputStream.write(responseEntity.getBody());
outputStream.flush();
} catch (IOException e) {
throw new ServletException(e);
}
}
}
private RequestEntity<byte[]> createRequestEntity(HttpServletRequest request, String url)
throws URISyntaxException, IOException {
String method = request.getMethod();
HttpMethod httpMethod = HttpMethod.resolve(method); // 1、封装请求头
MultiValueMap<String, String> headers = createRequestHeaders(request); // 2、封装请求体
byte[] body = createRequestBody(request); // 3、构造出RestTemplate能识别的RequestEntity
RequestEntity<byte[]> requestEntity = new RequestEntity<byte[]>(body, headers, httpMethod, new URI(url));
return requestEntity;
}
private byte[] createRequestBody(HttpServletRequest request) throws IOException {
InputStream inputStream = request.getInputStream();
return StreamUtils.copyToByteArray(inputStream);
}
private MultiValueMap<String, String> createRequestHeaders(HttpServletRequest request) {
HttpHeaders headers = new HttpHeaders();
List<String> headerNames = Collections.list(request.getHeaderNames());
for (String headerName : headerNames) {
List<String> headerValues = Collections.list(request.getHeaders(headerName));
for (String headerValue : headerValues) {
headers.add(headerName, headerValue);
}
}
return headers;
}
}
注意,上传中的操作使用了以下的依赖
<dependency>
<groupId>commons-fileupload</groupId>
<artifactId>commons-fileupload</artifactId>
<version>1.4</version>
</dependency>