Talk about SetStatusGatewayFilter of spring cloud gateway.

  springcloud

Order

This paper mainly studies SetStatusGatewayFilter of spring cloud gateway.

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 is created by default

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;
        }
    }

}
  • As you can see, the status of response is simply set according to the status.
  • Note that there are two options here. the source code notes option1 and uses option2 instead.

Example

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

Summary

SetStatusGatewayFilter simply sets the response. if option1 is used, it is set before the responsecommmit, and once it is set, it return directly and does not continue to go through the filter, so SetStatus can take effect. If option2 is used, it is executed in reverse filter order, and it will first determine whether to commit, and it will not be set until it is not committed, and it will not take effect.

doc