请求代理转发(三)

请求代理转发(三)

书接上回,在内部服务代理其他服务时, 这次使用 webclient 作为请求客户端

import static org.springframework.web.reactive.HandlerMapping.PATH_WITHIN_HANDLER_MAPPING_ATTRIBUTE;

@Slf4j
@RestController
public class ProxyController {

    @Autowired
    private WebClient.Builder builder;

    private WebClient webClient;

    public WebClient client() {
        if (webClient == null) {
            synchronized (ProxyController.class) {
                webClient = builder.build();
            }
        }
        return webClient;
    }


    /**
     *
     * @param prefix
     * @param exchange
     * @return
     */
    private String path(String prefix, ServerWebExchange exchange) {
        //获取 完整的请求 url
        String path = (String) exchange.getAttributes().get(PATH_WITHIN_HANDLER_MAPPING_ATTRIBUTE);
        return path.substring(prefix.length());
    }


    @RequestMapping("/proxy/**")
    public Mono<Void> proxy(ServerWebExchange exchange) {
        ServerHttpRequest request = exchange.getRequest();
        //拼接代理转发的 url
        URI newUri = rebuildURI(exchange);
        HttpMethod method = request.getMethod();

        WebClient.RequestBodySpec bodySpec = client()
                        .method(method).uri(newUri)
                        // copy header
                        .headers(httpHeaders -> httpHeaders.addAll(exchange.getRequest().getHeaders()));
        log.info("request url: {}", newUri);
        WebClient.RequestHeadersSpec<?> headersSpec;
        if (requiresBody(method)) {
            headersSpec = bodySpec.body(BodyInserters.fromDataBuffers(request.getBody()));
        } else {
            headersSpec = bodySpec;
        }
        return headersSpec.exchangeToMono(res -> {
            ServerHttpResponse response = exchange.getResponse();
            response.getHeaders().putAll(res.headers().asHttpHeaders());
            response.setStatusCode(res.statusCode());
            if (res == null) {
                return Mono.empty();
            }
            return exchange.getResponse().writeWith(res.body(BodyExtractors.toDataBuffers()));
        }).doOnError(t -> {
            log.error("proxy error: ", t);
        });
    }

    private URI rebuildURI(ServerWebExchange exchange) {
        URI original = exchange.getRequest().getURI();
        String path = path("/proxy", exchange);
        String scheme = "http";
        String host = "localhost";
        int port = 5556;
        //这个方式可以 copy get 请求的参数
        return UriComponentsBuilder.fromUri(original).scheme(scheme).host(host)
                .port(port).replacePath(path).build(containsEncodedParts(original)).toUri();
    }


    private boolean requiresBody(HttpMethod method) {
        switch (method) {
            case PUT:
            case POST:
            case PATCH:
                return true;
            default:
                return false;
        }
    }


    public static boolean containsEncodedParts(URI uri) {
        boolean encoded = (uri.getRawQuery() != null && uri.getRawQuery().contains("%"))
                || (uri.getRawPath() != null && uri.getRawPath().contains("%"));
        // Verify if it is really fully encoded. Treat partial encoded as unencoded.
        if (encoded) {
            try {
                UriComponentsBuilder.fromUri(uri).build(true);
                return true;
            } catch (IllegalArgumentException ignored) {
                if (log.isTraceEnabled()) {
                    log.trace("Error in containsEncodedParts", ignored);
                }
            }

            return false;
        }

        return encoded;
    }


}

测试

controller:

@GetMapping("/test")
public Mono<R<String>> hello2(@RequestParam String name) {
    System.out.println("name: " + name);
    if (name.equals("1")){
        throw new RuntimeException("异常了");
    }
    return Mono.justOrEmpty(R.ok("hello"));
}

访问代理: http://localhost:5556/proxy/test?name=tom, 结果:

{
    "data": "hello",
    "msg": "success",
    "code": 1
}

测试后发现 ,像 post 表单,json, 上传文件,都正常

good luck!

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值