遇到的一个需求,是前端请求落到A服务,然后A服务根据header里或者路径里某个字段,判断是走B服务还是C服务,但是不在网关层处理这种转发逻辑的话。
如果B、C服务都注册在同一个注册中心,使用RestTemplate利用服务名调用即可。
pom.xml
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<version>2.7.0</version>
</dependency>
<dependency>
<groupId>cn.hutool</groupId>
<artifactId>hutool-all</artifactId>
<version>5.8.0</version>
</dependency>
</dependencies>
A服务:通过过滤器拦截某个请求,转发到B服务,并将B服务的响应拿到后,返回给前端
package com.test;
import cn.hutool.http.HttpResponse;
import cn.hutool.http.HttpUtil;
import org.springframework.stereotype.Component;
import javax.servlet.*;
import javax.servlet.annotation.WebFilter;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
/**
* @author lucifer
* @description TODO
* @date 2022-09-22
*/
@WebFilter
@Component
public class TestFilter implements Filter {
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
System.out.println("进入doFilter");
HttpServletResponse response = (HttpServletResponse) servletResponse;
HttpResponse httpResponse = HttpUtil.createGet("http://localhost:8081/test2").execute();
ServletOutputStream outputStream = response.getOutputStream();
response.setHeader("Content-Disposition","attachment;filename=test.xls");
outputStream.write(httpResponse.bodyBytes());
outputStream.flush();
//。。。。。。省略
}
}
B服务:
package com.test2;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.io.IoUtil;
import cn.hutool.poi.excel.ExcelUtil;
import cn.hutool.poi.excel.ExcelWriter;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.List;
/**
* @author lucifer
* @description TODO
* @date 2022-09-22
*/
@RestController
public class TestController {
@GetMapping("test2")
public void test2(HttpServletResponse response) throws IOException {
List<String> row1 = CollUtil.newArrayList("aa", "bb", "cc", "dd");
List<String> row2 = CollUtil.newArrayList("aa1", "bb1", "cc1", "dd1");
List<String> row3 = CollUtil.newArrayList("aa2", "bb2", "cc2", "dd2");
List<String> row4 = CollUtil.newArrayList("aa3", "bb3", "cc3", "dd3");
List<String> row5 = CollUtil.newArrayList("aa4", "bb4", "cc4", "dd4");
List<List<String>> rows = CollUtil.newArrayList(row1, row2, row3, row4, row5);
// 通过工具类创建writer,默认创建xls格式
ExcelWriter writer = ExcelUtil.getWriter();
// 一次性写出内容,使用默认样式,强制输出标题
writer.write(rows, true);
//out为OutputStream,需要写出到的目标流
//response为HttpServletResponse对象
response.setContentType("application/vnd.ms-excel;charset=utf-8");
//test.xls是弹出下载对话框的文件名,不能为中文,中文请自行编码
response.setHeader("Content-Disposition", "attachment;filename=test.xls");
ServletOutputStream out = response.getOutputStream();
writer.flush(out, true);
// 关闭writer,释放内存
writer.close();
//此处记得关闭输出Servlet流
IoUtil.close(out);
}
}
测试结果:
通过调用 A服务,经过过滤器拦截,将请求转发到B服务,并将B服务的文件流,拿到后,响应给前端,并实现浏览器自动下载。
容易遇坑的地方:
刚开始我的filter是这样的:用的是RestTemplate,(好处是可以通过服务名访问,具体自行百度,这里就不做过多演示了),不是hutool里面的工具类,然后用RestTemplate,咋一看,好像没有啥毛病,然后测试发现,Excel损坏了。
@WebFilter
@Component
public class TestFilter implements Filter {
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
System.out.println("进入doFilter");
HttpServletResponse response = (HttpServletResponse) servletResponse;
RestTemplate restTemplate = new RestTemplate();
try {
RequestEntity requestEntity = new RequestEntity(HttpMethod.GET,new URI("http://localhost:8081/test2"));
ResponseEntity<String> res = restTemplate.exchange(requestEntity, String.class);
// HttpResponse httpResponse = HttpUtil.createGet("http://localhost:8081/test2").execute();
ServletOutputStream outputStream = response.getOutputStream();
response.setHeader("Content-Disposition","attachment;filename=test.xls");
outputStream.write(res.getBody().getBytes(StandardCharsets.UTF_8));
outputStream.flush();
} catch (Exception e) {
throw new RuntimeException(e);
}
}
}
测试结果:
发现它的文件大小比其它的大,并且打不开 。
再次修改:ok!!!
@WebFilter
@Component
public class TestFilter implements Filter {
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
System.out.println("进入doFilter");
HttpServletResponse response = (HttpServletResponse) servletResponse;
RestTemplate restTemplate = new RestTemplate();
try {
RequestEntity requestEntity = new RequestEntity(HttpMethod.GET, new URI("http://localhost:8081/test2"));
ResponseEntity<byte[]> res = restTemplate.exchange(requestEntity, byte[].class);
// HttpResponse httpResponse = HttpUtil.createGet("http://localhost:8081/test2").execute();
ServletOutputStream outputStream = response.getOutputStream();
response.setHeader("Content-Disposition", "attachment;filename=test.xls");
outputStream.write(res.getBody());
outputStream.flush();
} catch (Exception e) {
throw new RuntimeException(e);
}
}
}