本文主要研究下spring cloud gateway的SetStatusGatewayFilter

GatewayAutoConfiguration

spring-cloud-gateway-core-2.0.0.RC2-sources.jar!/org/springframework/cloud/gateway/config/GatewayAutoConfiguration.java

@Configuration
@ConditionalOnProperty(name = "spring.cloud.gateway.enabled", matchIfMissing = true)
@EnableConfigurationProperties
@AutoConfigureBefore(HttpHandlerAutoConfiguration.class)
@AutoConfigureAfter({GatewayLoadBalancerClientAutoConfiguration.class, GatewayClassPathWarningAutoConfiguration.class})
@ConditionalOnClass(DispatcherHandler.class)
public class GatewayAutoConfiguration {
    //......
    @Bean
    public SetStatusGatewayFilterFactory setStatusGatewayFilterFactory() {
        return new SetStatusGatewayFilterFactory();
    }
    //......
}
默认创建SetStatusGatewayFilterFactory

SetStatusGatewayFilterFactory

spring-cloud-gateway-core-2.0.0.RC2-sources.jar!/org/springframework/cloud/gateway/filter/factory/SetStatusGatewayFilterFactory.java

public class SetStatusGatewayFilterFactory extends AbstractGatewayFilterFactory<SetStatusGatewayFilterFactory.Config> {

    public static final String STATUS_KEY = "status";

    public SetStatusGatewayFilterFactory() {
        super(Config.class);
    }

    @Override
    public List<String> shortcutFieldOrder() {
        return Arrays.asList(STATUS_KEY);
    }

    @Override
    public GatewayFilter apply(Config config) {
        final HttpStatus status = ServerWebExchangeUtils.parse(config.status);
        final Integer intStatus;
        if (status == null) {
            intStatus = Integer.parseInt(config.status);
        } else {
            intStatus = null;
        }
        return (exchange, chain) -> {

            // option 1 (runs in filter order)
            /*exchange.getResponse().beforeCommit(() -> {
                exchange.getResponse().setStatusCode(finalStatus);
                return Mono.empty();
            });
            return chain.filter(exchange);*/

            // option 2 (runs in reverse filter order)
            return chain.filter(exchange).then(Mono.fromRunnable(() -> {
                // check not really needed, since it is guarded in setStatusCode,
                // but it's a good example
                if (!exchange.getResponse().isCommitted()) {
                    if (status != null) { // standard status
                        setResponseStatus(exchange, status);
                    } else if (intStatus != null && exchange.getResponse() instanceof AbstractServerHttpResponse) { //non-standard
                        ((AbstractServerHttpResponse)exchange.getResponse()).setStatusCodeValue(intStatus);
                    }
                }
            }));
        };
    }

    public static class Config {
        //TODO: relaxed HttpStatus converter
        private String status;

        public String getStatus() {
            return status;
        }

        public void setStatus(String status) {
            this.status = status;
        }
    }

}
  • 可以看到,就是简单地根据status来设置response的status
  • 注意这里有两个option,源码注释掉了option1,改为使用option2

实例

spring:
  cloud:
    gateway:
      routes:
      - id: setstatusstring_route
        uri: http://example.org
        predicates:
        - Path=/foo/**
        filters:
        - SetStatus=BAD_REQUEST
      - id: setstatusint_route
        uri: http://example.org
        predicates:
        - Path=/name/**
        filters:
        - SetStatus=401

小结

SetStatusGatewayFilter就是简单地设置response,使用option1方式的话,是在response commit之前设置的,而且一旦设置就直接return,不再继续走filter了,因而SetStatus可以生效;使用option2的话,是逆filter顺序执行的,会先判断是否commit,未commit才设置,实际不生效。

doc


codecraft
11.9k 声望2k 粉丝

当一个代码的工匠回首往事时,不因虚度年华而悔恨,也不因碌碌无为而羞愧,这样,当他老的时候,可以很自豪告诉世人,我曾经将代码注入生命去打造互联网的浪潮之巅,那是个很疯狂的时代,我在一波波的浪潮上留下...