An open API service indexing awesome lists of open source software.

https://github.com/chenggangpro/spring-cloud-gateway-plugin

Spring Cloud Gateway Extra Plugin
https://github.com/chenggangpro/spring-cloud-gateway-plugin

spring-cloud springcloudgateway springcloudgreenwich

Last synced: 5 months ago
JSON representation

Spring Cloud Gateway Extra Plugin

Awesome Lists containing this project

README

          

# WARNING
> This Project Is Archives,Can't catch up the spring-cloud-version.But the code and the thought is much more useful for who want adapt spring-cloud-gateway to his own project
# spring-cloud-gateway-plugin
Spring Cloud Gateway Extra Plugin

[![Build Status](https://travis-ci.com/chenggangpro/spring-cloud-gateway-plugin.svg?branch=master)](https://travis-ci.com/chenggangpro/spring-cloud-gateway-plugin)

## Current Version

|Gateway Version|Plugin Version|
|:--|:--|
|`Greenwich.SR1`|`2.1.SR1.3.RELEASE`|
|`Greenwich.SR2`|`2.1.SR2.2.RELEASE`|

> More Detail See [Wiki](https://github.com/chenggangpro/spring-cloud-gateway-plugin/wiki)

### This Plugin Support Below Features:

* [x] Cache Request Body And Form Body
* [x] Add Request Log Filter To Log Request And Response
* [x] Add Read Json Response Body And Log Response Body
* [x] Add Global Exception Handler With Json
* [x] Add Custom Exception Handler
* [x] Add Grey Route With Ribbon And Eureka
* [x] Each Route Use different Hystrix CommandKey
* [x] Support Dynamic Predicate With Existing Routes

### How To Use This Feature

##### Note:

* This Dependency Base Spring Cloud Gateway[`Greenwich.SR1`],Suggest To Update To This SpringCloud Version,Official Resolve Some Issues , Fix Some Bugs.
* The SpringBoot Version need to update to [`2.1.6.RELEASE`],It fix reactor-netty issues
* This Dependency Is Now In Maven Central.
* The Feature To Read Request And Response Json Data Will Loss A Lot Of Performance,It Will Reduce The Gateway Traffic.

##### Step

* 1 . Include Dependency

> Spring Cloud Gateway

```xml


org.springframework.cloud
spring-cloud-starter-gateway



org.springframework.cloud
spring-cloud-starter-netflix-eureka-client




org.springframework.cloud
spring-cloud-dependencies
Greenwich.SR1
pom
import



```
> Gateway Plugin

```xml

pro.chenggang
spring-cloud-gateway-plugin
2.1.SR1.1.RELEASE

```

* 2 . Add Enable Annotation To You Application

```java
@EnableGatewayPlugin
public class SpringCloudGatewayPluginApplication {

public static void main(String[] args) {
SpringApplication.run(SpringCloudGatewayPluginApplication.class, args);
}

}
```

Note: If You User SpringBoot 2.1.0+,You should set `allow-bean-definition-overriding` to `true`

```yaml
spring:
main:
allow-bean-definition-overriding: true
```

* 3 . Choose Plugin Feature To Use

By use this annotation `@EnableGatewayPlugin` to enable the plugin,the plugin support switch to choose feature
By default,the `GatewayContext Filter` is always into system

```yaml
spring:
profiles:
active: dev
cloud:
gateway:
plugin:
config:
log-request: true
read-request-data: true # this setting will read all request data
read-response-data: true
exception-json-handler: true
enable-dynamic-route: true
grey:
enable: false
grey-ribbon-rule: weight_response
```
* 4 . Specific Setting To Enable Read Request Data

```yaml
spring:
cloud:
gateway:
plugin:
config:
read-request-data-service-id-list: #set specific serviceId from discover to read request Data
- serviceId1
- serviceId2
read-request-data-path-list: #set specific path to read request data
- /service/path1/*
- /service/path2/**
- /service/path3
```

* 5 . User GatewayContext

You Can Use GatewayContext To Get Cache JsonBody Or Form Body,Just Use

```java
GatewayContext gatewayContext = exchange.getAttribute(GatewayContext.CACHE_GATEWAY_CONTEXT);
```
* 6 . The Deference Between `GreyRibbonRule.DEFAULT` And `GreyRibbonRule.WeightResponse`

The Default GreyRibbonRule Just Use Round Rule As Base Ribbon Rule
The WeightResponse GreyRibbonRule Use WeightResponse Rule As Base Ribbon Rule

* 7 . The Grey Route

* Setup Gateway Properties

```yaml
spring:
cloud:
gateway:
plugin:
grey:
grey-rule-list:
- service-id: privider1
version: 2.0.0
operation: OR
rules:
- key: key1
value:
- value1
- value2
- value3
- key: key2
value:
- value4
- value5
- value6
- service-id: provider2
version: 2.0.0
operation: AND
rules:
- key: keya
value:
- value1a
- value2a
- value3a
- key: keyb
value:
- value4b
- value5b
- value6b

```

* Set Up Service MetaInfo

```yaml
#proiver1
eureka:
instance:
metadata-map:
version: 2.0.0
```

* The Properties Active Rule Principle

When Request URL Route To Provider1,When The Request JsonBody Or Form Data Contain The Key ->`Key1` And Match Any Of The Value->[`value1`,`value2`,`value3`]
The Route The Request To The Service Which Setup The MetaInfo With Specific Version Which Match The Gateway Grey Setup

* 8 . How To Custom GlobalException Handler With Json

In Order To Handle Other Exception,You Can Define Specific Bean Implements `ExceptionHandlerStrategy`
By default,plugin supply `DefaultExceptionHandlerStrategy` In Case Of None Strategy Exist

```java
@Slf4j
public class DefaultExceptionHandlerStrategy implements ExceptionHandlerStrategy {

@Override
public Class getHandleClass() {
return Throwable.class;
}

@Override
public ExceptionHandlerResult handleException(Throwable throwable) {
ResponseResult responseResult = new ResponseResult<>(SystemResponseInfo.GATEWAY_ERROR,throwable.getMessage());
ExceptionHandlerResult result = new ExceptionHandlerResult(HttpStatus.INTERNAL_SERVER_ERROR, JSON.toJSONString(responseResult));
log.debug("[DefaultExceptionHandlerStrategy]Handle Exception:{},Result:{}",throwable.getMessage(),result);
log.error("[DefaultExceptionHandlerStrategy]Log Exception In Error Level,Exception Message:{}",throwable.getMessage());
return result;
}
}
```

Or You Can Use `@ExceptionHandler` just like below,

The`@ResponseStatus` is optional,if you don't add `@ResponseStatus`,the default HttpStatus is HttpStatus.BAD_GATEWAY

```java
@Component
public class DemoExceptionHandler {

@ExceptionHandler({NotFoundException.class})
@ResponseStatus(HttpStatus.NOT_FOUND)
public Map handlerException(ServerWebExchange exchange,TimeoutException throwable){
LinkedHashSet originalRequestUris = exchange.getAttributeOrDefault(ServerWebExchangeUtils.GATEWAY_ORIGINAL_REQUEST_URL_ATTR,null);
Map map = Maps.newHashMapWithExpectedSize(2);
map.put("URI",originalRequestUris);
map.put("ExceptionMessage",throwable.getClass().getSimpleName());
return map;
}

@ExceptionHandler({ClientException.class, TimeoutException.class})
@ResponseStatus(HttpStatus.BAD_GATEWAY)
public Map handlerException(ServerWebExchange exchange,TimeoutException throwable){
LinkedHashSet originalRequestUris = exchange.getAttributeOrDefault(ServerWebExchangeUtils.GATEWAY_ORIGINAL_REQUEST_URL_ATTR,null);
Map map = Maps.newHashMapWithExpectedSize(2);
map.put("URI",originalRequestUris);
map.put("ExceptionMessage",throwable.getClass().getSimpleName());
return map;
}
}
```

* 9 . How To Use Dynamic Predicate With Existing Routes

* Define A Component Implements `DynamicRouteProcessor`,this processor process serverWebExchange for dynamic route predicate

> the `DynamicRouteProcessor` definition

```java
/**
* Process ServerWebExchange for dynamic route predicate
* @author chenggang
* @date 2019/07/17
*/
public interface DynamicRouteProcessor {

/**
* preprocess action
* @param exchange ServerWebExchange
* @return process Result ,if result is Optional.empty(),then dynamic predicate not working
*/
Optional> preprocess(ServerWebExchange exchange);

/**
* process to unify config for predicate
* @param preprocessResult pre process result
* @param route current route
* @return
*/
Optional processConfig(PreprocessResult preprocessResult, Route route);

/**
* target predicate bean 's class
* @return RoutePredicateFactory Class
*/
Optional> targetPredicateBeanClass();
}
```

> custom dynamic route processor

```java
@Component
public class CustomDynamicRouteProcessor implements DynamicRouteProcessor {

@Override
public Optional preprocess(ServerWebExchange exchange) {
String route = exchange.getRequest().getHeaders().getFirst("route");
if(StringUtils.isBlank(route)){
return Optional.of(PreprocessResult.builder().result(false).build());
}
return Optional.of(PreprocessResult.builder().result(true).resultData(route).build());
}

@Override
public Optional processConfig(PreprocessResult preprocessResult, Route route) {
if(!preprocessResult.getResult()){
return Optional.empty();
}
Object resultData = preprocessResult.getResultData();
if(!(resultData instanceof String)){
return Optional.empty();
}
String data = (String) resultData;
DemoDynamicRoutePredicateFactory.Config config = DemoDynamicRoutePredicateFactory.Config.builder().header(data).route(route).build();
return Optional.of(config);
}

@Override
public Optional> targetPredicateBeanClass() {
return Optional.of(DemoDynamicRoutePredicateFactory.class);
}
}

```

> Define a `AbstractRoutePredicateFactory` ,the `Config` Class Must Implements `DynamicRouteConfig`

```java
@Slf4j
@Component
public class DemoDynamicRoutePredicateFactory extends AbstractRoutePredicateFactory {

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

@Override
public Predicate apply(Config config) {
return exchange -> {
Route route = config.getRoute();
if(Objects.isNull(route.getUri())){
log.debug("Route Uri Is NUll Return False,RouteID:{}",route.getId());
return false;
}
String routeUriHost = route.getUri().getHost();
String headerData = config.getHeader();
if(StringUtils.isBlank(routeUriHost) || StringUtils.isBlank(headerData)){
log.debug("Route Uri Host Or HeaderData Is Blank Return False,RouteID:{}",route.getId());
return false;
}
if(routeUriHost.equalsIgnoreCase(headerData)){
log.debug("Route Uri Host Matched Header Data Return True,RouteID:{}",route.getId());
route.getFilters();
return true;
}
log.debug("Route Uri Not Matched Return False,RouteID:{}",route.getId());
return false;
};
}

@Getter
@Setter
@Builder
@ToString
@AllArgsConstructor
public static class Config implements DynamicRouteConfig {

private Route route;
private String header;

}
}
```

This Feature Support dynamic predicate with existing routes,Fox example: You can according the custom header to match the loadbalance route,

* More logical detail to see `DynamicRoutePredicateHandlerMapping`