自定义Handler
Handler
从请求头中获取username
和password
,模拟登陆请求
@Component
public class UserHandler {
private UserService userService;
public UserHandler(UserService userService) {
this.userService = userService;
}
public Mono<ServerResponse> login(ServerRequest serverRequest) {
return ServerResponse.ok()
.contentType(MediaType.APPLICATION_JSON)
.body(Mono.fromSupplier(()->{
String username = serverRequest.headers().firstHeader("username");
String password = serverRequest.headers().firstHeader("password");
try{
boolean success = userService.login(username, password);
return new Return(success,username+":"+password);
}catch (Exception e) {
return new Return(false,e.getMessage());
}
}), Return.class);
}
//构造返回对象
@Data
@AllArgsConstructor
public static class Return{
private boolean success;
private String msg;
}
}
Service
@Component
public class UserService {
public boolean login(String username,String password) {
//todo form db;
return true;
}
}
Router
@Bean
public RouterFunction<ServerResponse> userRouter(UserHandler userhandler) {
return nest(
//相当于controller上的requestmapping
path("/user"),
route(GET("/login"), userhandler::login)
// .andRoute(GET("/date"), userha::getDate);
);
}
测试
自定义 Predicate
factory
自定义HeaderTokenRoutePredicateFactory
,用来拦截所有请求头中必须包含指定的header
public class HeaderTokenRoutePredicateFactory extends AbstractRoutePredicateFactory<HeaderTokenRoutePredicateFactory.Config> {
public HeaderTokenRoutePredicateFactory() {
super(Config.class);
}
@Override
public List<String> shortcutFieldOrder() {
return Arrays.asList("headers", "strategy");
}
@Override
public Predicate<ServerWebExchange> apply(Config config) {
return new GatewayPredicate() {
@Override
public boolean test(ServerWebExchange exchange) {
HttpHeaders httpHeaders = exchange.getRequest().getHeaders();
List<String> headers = config.getHeaders();
if (headers == null || headers.isEmpty()) {
return true;
}
switch (config.getStrategy()) {
case AND:
//策略是AND 时,请求头中必须包含所有header
return headers.stream().allMatch(header -> existHeaderFunc.apply(httpHeaders,header));
default:
//策略为OR时, 请求头包含任意一header即可
return headers.stream().anyMatch(header -> existHeaderFunc.apply(httpHeaders,header));
}
}
@Override
public String toString() {
return String.format("Config: %s", config.toString());
}
};
}
BiFunction<HttpHeaders, String, Boolean> existHeaderFunc = (httpHeaders, header) -> Optional.ofNullable(httpHeaders.get(header)).filter(list -> !list.isEmpty()).isPresent();
public enum Strategy {
AND, OR;
}
public static class Config {
//配置的header, 可以配置多个
private List<String> headers = new ArrayList<>();
//当配置多个时,校验策略, and 还是 or
private Strategy strategy = Strategy.OR;
public List<String> getHeaders() {
return headers;
}
public Strategy getStrategy() {
return strategy;
}
public Config setHeaders(List<String> ignorePatterns) {
this.headers = ignorePatterns;
return this;
}
public void setStrategy(Strategy strategy) {
this.strategy = strategy;
}
@Override
public String toString() {
return new ToStringCreator(this).append("headers", headers).append("strategy", strategy).toString();
}
}
}
configuration配置
@Bean
public HeaderTokenRoutePredicateFactory headerTokenRoutePredicateFactory() {
return new HeaderTokenRoutePredicateFactory();
}
yaml配置
配置predicate:类型为HeaderToken
spring:
cloud:
gateway:
routes:
- id: header_token_route
uri: forward:///user/login
predicates:
- name: HeaderToken
args:
# 所有请求,只要请求头中包含username,password
## 就 forward:///user/login
headers: username,password
strategy: AND
测试
访问一个不存在的地址/xyz
测试:
自定义 Filter
PreLog
preLog
向请求头中添加属性,记录请求开始时间。
public class PreLogGatewayFilterFactory extends AbstractGatewayFilterFactory<PreLogGatewayFilterFactory.Config> implements Ordered {
@Override
public List<String> shortcutFieldOrder() {
return Arrays.asList("level");
}
public static final String REQUEST_TIME_IN_KEY = "request_time_in";
public PreLogGatewayFilterFactory() {
super(Config.class);
}
@Override
public GatewayFilter apply(Config config) {
return new GatewayFilter() {
@Override
public Mono<Void> filter(ServerWebExchange exchange,
GatewayFilterChain chain) {
//请求开始前记录时间
if( config.getLevel() == Level.DEBUG) {
System.out.println("------------------------preLog开始处理请求....."+ LocalDateTime.now());
}
// 无法直接操作 header,会抛出 ReadOnlyHttpHeaders.add xxx 异常
// exchange.getRequest().getHeaders().add(REQUEST_TIME_IN_KEY,String.valueOf(System.currentTimeMillis()));
ServerHttpRequest changedRequest = exchange.getRequest()
.mutate()
.header(REQUEST_TIME_IN_KEY,String.valueOf(System.currentTimeMillis()))
.build();
return chain.filter(exchange.mutate().request(changedRequest).build());
}
@Override
public String toString() {
return filterToStringCreator(
PreLogGatewayFilterFactory.this)
.append(config.getLevel())
.toString();
}
};
}
public enum Level {
DEBUG, INFO;
}
public static class Config {
private Level level =Level.DEBUG;
public Level getLevel() {
return level;
}
public Config setLevel(Level level) {
this.level = level;
return this;
}
}
@Override
public int getOrder() {
return 100;
}
}
PostLog
PostLog
计算整个请求耗时:
@Override
public GatewayFilter apply(Config config) {
return new GatewayFilter() {
@Override
public Mono<Void> filter(ServerWebExchange exchange,
GatewayFilterChain chain) {
return chain.filter(exchange).then(Mono.fromRunnable(()-> {
Optional.ofNullable(exchange.getRequest().getHeaders().get(REQUEST_TIME_IN_KEY)).ifPresent(list ->{
Long startTime = Long.parseLong(list.get(0));
if( config.getLevel() == Level.INFO) {
System.out.println("----------------------耗时::" + (System.currentTimeMillis() - startTime));
}
});
}));
}
@Override
public String toString() {
return filterToStringCreator(
PostLogGatewayFilterFactory.this)
.append(config.getLevel())
.toString();
}
};
}
configuration配置
@Bean
public PostLogGatewayFilterFactory postLogGatewayFilterFactory() {
return new PostLogGatewayFilterFactory();
}
@Bean
public PreLogGatewayFilterFactory PreLogGatewayFilterFactory() {
return new PreLogGatewayFilterFactory();
}
yaml配置
spring:
cloud:
gateway:
routes:
- id: header_token_route
uri: forward:///user/login
predicates:
- name: HeaderToken
args:
headers: username,password
strategy: AND
filters:
- name: PreLog
args:
level: DEBUG
- PostLog=INFO
测试
请求完毕后,后台会记录请求耗时日志:
自定义 GlobalFilter
GlobalFilter 比较简单, 只需要实现GlobalFilter
接口,并注入容器即可。
@Component
public class MyGlobalFilter implements GlobalFilter, Ordered {
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
//记录所有请求信息
URI url = exchange.getRequest().getURI();
System.out.println("----------["+ LocalDateTime.now() +"]处理请求:"+ url.getRawPath() );
return chain.filter(exchange);
}
@Override
public int getOrder() {
return 10;
}
}