请求代理转发(三)
书接上回,在内部服务代理其他服务时, 这次使用 webclient
作为请求客户端
import static org.springframework.web.reactive.HandlerMapping.PATH_WITHIN_HANDLER_MAPPING_ATTRIBUTE;@Slf4j
@RestController
public class ProxyController {@Autowiredprivate 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) {//获取 完整的请求 urlString 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();//拼接代理转发的 urlURI 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!