侧边栏壁纸
博主头像
木易成

聊聊软件、聊聊金融、聊聊篮球

  • 累计撰写 29 篇文章
  • 累计创建 18 个标签
  • 累计收到 3 条评论

目 录CONTENT

文章目录

SpringCloud微服务模拟nginx转发

木易成
2023-03-21 / 0 评论 / 0 点赞 / 2,930 阅读 / 605 字

两个服务A、B,B不需要鉴权,A需要鉴权访问。客户端如果直接访问B会存在一定的风险。所以希望通过A访问B,这时候就需要A实现一个类似nginx网关转发功能

实现方式

在controller中拦截指定路径,通过转换地址,封装请求体,使用RestTemplate重发请求

实现代码

1、配置RestTemplate

@Configuration
public class BaseConfig {
    
    
    @Bean(value = "routeRestTemplate")
    RestTemplate routeRestTemplate(){
        SimpleClientHttpRequestFactory factory = new SimpleClientHttpRequestFactory();
        // 读写超时时间/毫秒
        factory.setReadTimeout(20 * 1000);
        // 连接超时时间/毫秒
        factory.setConnectTimeout(5 * 1000);
        RestTemplate restTemplate = new RestTemplate(factory);
        restTemplate.setErrorHandler(new ResponseErrorHandler(){
            @Override
            public boolean hasError(ClientHttpResponse response) {
                // 返回false表示不管response的status是多少都返回没有错
                // 这里可以自己定义那些status code你认为是可以抛Error
                return false;
            }
            @Override
            public void handleError(ClientHttpResponse response) {
                // 这里面可以实现你自己遇到了Error进行合理的处理
            }
        });
        return restTemplate;
    }
}

2.编写路由工具类

直接上代码

@Slf4j
@Component
public class RoutingDelegate {
    
    @Resource
    @Qualifier("routeRestTemplate")
    private  RestTemplate routeRestTemplate;
    
    
    public ResponseEntity<String> redirect(HttpServletRequest request, HttpServletResponse response,String routeUrl, String prefix) {
        try {
            String redirectUrl = createRedictUrl(request,routeUrl, prefix);
            RequestEntity requestEntity = createRequestEntity(request, redirectUrl);
            log.info("路由转发,跳转地址:{},请求数据:{}",redirectUrl,requestEntity);
            ResponseEntity<String> responseEntity = route(requestEntity);
            log.info("路由转发,返回结果:{}",responseEntity);
            return responseEntity;
        } catch (Exception e) {
            log.info("路由转发请求失败", e);
            throw  new HousekeeperException(HousekeeperErrorEnums.BaseCode.API_REQUEST_ERROR);
        }
    }
    
    private String createRedictUrl(HttpServletRequest request, String routeUrl, String prefix) {
        String queryString = request.getQueryString();
        return routeUrl + request.getRequestURI().replace(prefix, "") +
                (queryString != null ? "?" + queryString : "");
    }
    
    
    private RequestEntity createRequestEntity(HttpServletRequest request, String url) throws URISyntaxException, IOException {
        String method = request.getMethod();
        HttpMethod httpMethod = HttpMethod.resolve(method);
        MultiValueMap<String, String> headers = parseRequestHeader(request);
        byte[] body = parseRequestBody(request);
        return new RequestEntity<>(body, headers, httpMethod, new URI(url));
    }
    
    private ResponseEntity<String> route(RequestEntity requestEntity) {
        return routeRestTemplate.exchange(requestEntity, String.class);
    }
    
    
    private byte[] parseRequestBody(HttpServletRequest request) throws IOException {
        InputStream inputStream = request.getInputStream();
        return StreamUtils.copyToByteArray(inputStream);
    }
    
    private MultiValueMap<String, String> parseRequestHeader(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;
    }
}

3、编写controller转发

@RefreshScope
@RestController
@RequestMapping(RouteController.DELEGATE_PREFIX)
public class RouteController {
    
    public final static String DELEGATE_PREFIX = "/payRoute";
    
    @Value("${route.pay.url:}")
    private String redirectUrl;
    
    @Resource
    private RoutingDelegate routingDelegate;
    
    @RequestMapping(value = "/**", method = {RequestMethod.GET, RequestMethod.POST, RequestMethod.PUT, RequestMethod.DELETE}, produces = MediaType.TEXT_PLAIN_VALUE)
    public ResponseEntity catchAll(HttpServletRequest request, HttpServletResponse response) {
        return routingDelegate.redirect(request, response, redirectUrl, RouteController.DELEGATE_PREFIX);
    }
}

总结

如上可以实现通过服务实现类似nginx或gateway的网关转发功能

0

评论区