小强哥博客

小强哥,小强哥博客,技术大咖

spring cloud gateway踩坑记录(三)

由于项目需要,最近开始使用springgatway,这篇文章主要记录下遇到的坑。

springboot使用,如下,

<parent>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-parent</artifactId>
    <version>2.0.5.RELEASE</version>
    <relativePath/>
  </parent>

springcloud gateway,如下,

<dependency>
      <groupId>org.springframework.cloud</groupId>
      <artifactId>spring-cloud-starter-gateway</artifactId>
      <version>2.0.0.RELEASE</version>
    </dependency>

springcloud使用,如下,

<dependencyManagement>
    <dependencies>
      <dependency>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-dependencies</artifactId>
        <version>Finchley.RELEASE</version>
        <type>pom</type>
        <scope>import</scope>
      </dependency>
    </dependencies>
  </dependencyManagement>

第三个坑,如何改变请求时接口的queryParameter参数?有人肯定会想为什么需要改变这参数,我当时接触到这需求也是这么疑问,不过后来想想也对,网关既然是所有程序的前置服务,就应该保证后面接口的入参、出参的完整性。不多说废话了,直接上解决方,也很简单,如下。

首先定义一个filte,如下,该filter的作用是拦截服务端请求,并在请求发送之前改变请求参数,

import com.google.common.net.UrlEscapers;
import java.net.URI;
import org.springframework.cloud.gateway.filter.GatewayFilter;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.core.Ordered;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;


public class GetParamFilter implements GatewayFilter, Ordered {

  @Override
  public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
    URI uri = generateURI(exchange);
    ServerHttpRequest request = exchange.getRequest().mutate().uri(uri).build();
    ServerWebExchange build = exchange.mutate().request(request).build();
    return chain.filter(build);
  }

  @Override
  public int getOrder() {
    return 0;
  }

  private URI generateURI(ServerWebExchange exchange) {
    URI reqUri = exchange.getRequest().getURI();
    StringBuffer queryBuf = new StringBuffer();
    String uriFmt = "%s://%s:%s%s%s";
    String scheme = reqUri.getScheme();
    String host = reqUri.getHost();
    String port = String.valueOf(reqUri.getPort());
    String path = reqUri.getPath();
    queryBuf.append("?1=1");
    if (reqUri.getQuery() != null) {
      queryBuf.append(UrlEscapers.urlPathSegmentEscaper().escape("&" + reqUri.getQuery()));
    }
    queryBuf.append(UrlEscapers.urlPathSegmentEscaper().escape("&myId=10000"));
    URI uri = URI
        .create(String.format(uriFmt, new Object[]{scheme, host, port, path, queryBuf.toString()}));
    return uri;
  }
}

接着在route中配置该filter,如下,

.route(
    r -> r.path("/v1/user/phone").and()
        .method(
            HttpMethod.GET).filters(f -> f.filter(new GetParamFilter()))
        .uri("lb://pacaya")
)

我们在通过测试访问:http://127.0.0.1:8080/v1/user/phone,实际上经过上述filter会把请求改变成http://127.0.0.1:8080/v1/user/phone?1=1&myId=10000

在这filter中有个需要注的地方,如果参数中含有中文或者特殊符号需要通过UrlEscapers.urlPathSegmentEscaper().escape("&myId=10000")进行转码,否则后台接会报错。

第二个需要注意的地方,不能直接通过exchange.getRequest().getQueryParams().set();的方式设置、添加参数,因为getQueryParams返回的是一个只读的数据,如下说明,

/**
	 * Return a read-only map with parsed and decoded query parameter values.
	 */
	MultiValueMap<String, String> getQueryParams();

完。