springboot过滤器转发请求到其它服务,并响应前端

本文介绍了如何在A服务中通过过滤器拦截请求并转发到B服务,同时处理B服务返回的文件流,实现前端自动下载。在初始尝试中,由于使用RestTemplate不当导致Excel文件损坏,最终通过调整代码,正确地使用RestTemplate获取并传递byte[]解决了问题。

     遇到的一个需求,是前端请求落到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);
        }
    }
}

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值