From 783a3f9946deb1b2079963254bcf180918e35fef Mon Sep 17 00:00:00 2001 From: Wenchao Gong Date: Mon, 18 Oct 2021 23:59:57 +0800 Subject: [PATCH] =?UTF-8?q?=E5=BC=82=E5=B8=B8=E5=85=A8=E5=B1=80=E5=A4=84?= =?UTF-8?q?=E7=90=86?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../ruoyi/gateway/config/GatewayConfig.java | 7 ++ .../GlobalErrorWebExceptionHandler.java | 76 +++++++++++++++++++ .../constant/ErrorMessageConstant.java | 15 ++++ .../gateway/exception/AkRequireException.java | 7 +- .../ContentTypeNotSupportedException.java | 7 +- .../gateway/exception/GatewayException.java | 30 +++++++- .../MethodNotSupportedException.java | 7 +- .../gateway/exception/RateLimitException.java | 20 ++--- .../exception/RuleExpiredException.java | 7 +- .../exception/RuleNotExistException.java | 7 +- .../gateway/filter/CustomerGlobalFilter.java | 6 +- 11 files changed, 156 insertions(+), 33 deletions(-) create mode 100644 ruoyi-extend/ruoyi-isc-gateway/src/main/java/com/ruoyi/gateway/config/handler/GlobalErrorWebExceptionHandler.java create mode 100644 ruoyi-extend/ruoyi-isc-gateway/src/main/java/com/ruoyi/gateway/constant/ErrorMessageConstant.java diff --git a/ruoyi-extend/ruoyi-isc-gateway/src/main/java/com/ruoyi/gateway/config/GatewayConfig.java b/ruoyi-extend/ruoyi-isc-gateway/src/main/java/com/ruoyi/gateway/config/GatewayConfig.java index 29143581c..ca4a9f4ba 100644 --- a/ruoyi-extend/ruoyi-isc-gateway/src/main/java/com/ruoyi/gateway/config/GatewayConfig.java +++ b/ruoyi-extend/ruoyi-isc-gateway/src/main/java/com/ruoyi/gateway/config/GatewayConfig.java @@ -1,10 +1,12 @@ package com.ruoyi.gateway.config; +import com.ruoyi.gateway.config.handler.GlobalErrorWebExceptionHandler; import com.ruoyi.gateway.config.provider.RedisRouteDefinitionRepository; import com.ruoyi.gateway.filter.CustomerGlobalFilter; import com.ruoyi.gateway.ratelimit.CustomerRedisRateLimiter; import org.redisson.api.RedissonClient; import org.springframework.beans.factory.annotation.Qualifier; +import org.springframework.boot.web.reactive.error.ErrorWebExceptionHandler; import org.springframework.cloud.gateway.filter.GlobalFilter; import org.springframework.cloud.gateway.filter.ratelimit.RedisRateLimiter; import org.springframework.cloud.gateway.route.RouteDefinitionRepository; @@ -53,4 +55,9 @@ public class GatewayConfig { return new CustomerGlobalFilter(customerRedisRateLimiter); } + + @Bean + public ErrorWebExceptionHandler errorWebExceptionHandler() { + return new GlobalErrorWebExceptionHandler(); + } } diff --git a/ruoyi-extend/ruoyi-isc-gateway/src/main/java/com/ruoyi/gateway/config/handler/GlobalErrorWebExceptionHandler.java b/ruoyi-extend/ruoyi-isc-gateway/src/main/java/com/ruoyi/gateway/config/handler/GlobalErrorWebExceptionHandler.java new file mode 100644 index 000000000..708af930d --- /dev/null +++ b/ruoyi-extend/ruoyi-isc-gateway/src/main/java/com/ruoyi/gateway/config/handler/GlobalErrorWebExceptionHandler.java @@ -0,0 +1,76 @@ +package com.ruoyi.gateway.config.handler; + +import cn.hutool.core.map.MapUtil; +import cn.hutool.core.util.StrUtil; +import cn.hutool.json.JSONUtil; +import lombok.extern.slf4j.Slf4j; +import org.springframework.boot.web.reactive.error.ErrorWebExceptionHandler; +import org.springframework.core.annotation.Order; +import org.springframework.core.io.buffer.DataBufferFactory; +import org.springframework.http.HttpStatus; +import org.springframework.http.MediaType; +import org.springframework.http.server.reactive.ServerHttpResponse; +import org.springframework.web.server.ResponseStatusException; +import org.springframework.web.server.ServerWebExchange; +import org.springframework.web.server.handler.ResponseStatusExceptionHandler; +import reactor.core.publisher.Mono; + +import java.nio.charset.StandardCharsets; +import java.util.HashMap; +import java.util.Map; +import java.util.Optional; + +/** + * 网关异常通用处理器,只作用在webflux 环境下 , 优先级低于 {@link ResponseStatusExceptionHandler} 执行 + * @author Wenchao Gong + * @date 2021-10-18 + */ +@Slf4j +@Order(-1) +public class GlobalErrorWebExceptionHandler implements ErrorWebExceptionHandler { + + @Override + public Mono handle(ServerWebExchange exchange, Throwable ex) + { + ServerHttpResponse response = exchange.getResponse(); + + if (response.isCommitted()) { + return Mono.error(ex); + } + + // header set + response.getHeaders().setContentType(MediaType.APPLICATION_JSON); + if (ex instanceof ResponseStatusException) { + final ResponseStatusException statusException = (ResponseStatusException) ex; + response.setStatusCode(statusException.getStatus()); + } + Optional code = Optional.ofNullable(response.getRawStatusCode()); + final String message = getMessage(ex); + + return response.writeWith(Mono.fromSupplier(() -> { + DataBufferFactory bufferFactory = response.bufferFactory(); + return bufferFactory.wrap(JSONUtil.toJsonStr(result(code, message)) + .getBytes(StandardCharsets.UTF_8)); + })); + } + + private String getMessage(Throwable ex) + { + if(ex instanceof ResponseStatusException) { + String reason = ((ResponseStatusException) ex).getReason(); + log.info(reason); + return reason; + } + String message = ex.getMessage(); + log.error(message, ex); + return message; + } + + private Map result(Optional code, String message) { + final HashMap result = MapUtil.newHashMap(3); + result.put("code", code.orElse(HttpStatus.INTERNAL_SERVER_ERROR.value())); + result.put("message", message); + result.put("data", null); + return result; + } +} diff --git a/ruoyi-extend/ruoyi-isc-gateway/src/main/java/com/ruoyi/gateway/constant/ErrorMessageConstant.java b/ruoyi-extend/ruoyi-isc-gateway/src/main/java/com/ruoyi/gateway/constant/ErrorMessageConstant.java new file mode 100644 index 000000000..b7811d093 --- /dev/null +++ b/ruoyi-extend/ruoyi-isc-gateway/src/main/java/com/ruoyi/gateway/constant/ErrorMessageConstant.java @@ -0,0 +1,15 @@ +package com.ruoyi.gateway.constant; + +/** + * 错误消息常量 + * @author Wenchao Gong + * @date 2021-10-18 + */ +public interface ErrorMessageConstant { + String AK_REQUIRE = "没有找到授权码"; + String CONTENT_TYPE_NOT_SUPPORTED = "请求ContentType不支持"; + String METHOD_NOT_SUPPORTED = "请求Method不支持"; + String RATE_LIMIT = "调用次数过多"; + String RULE_EXPIRED = "规则已过期"; + String RULE_NOT_EXIST = "规则不存在"; +} diff --git a/ruoyi-extend/ruoyi-isc-gateway/src/main/java/com/ruoyi/gateway/exception/AkRequireException.java b/ruoyi-extend/ruoyi-isc-gateway/src/main/java/com/ruoyi/gateway/exception/AkRequireException.java index 39747e4c4..1e4f47a7c 100644 --- a/ruoyi-extend/ruoyi-isc-gateway/src/main/java/com/ruoyi/gateway/exception/AkRequireException.java +++ b/ruoyi-extend/ruoyi-isc-gateway/src/main/java/com/ruoyi/gateway/exception/AkRequireException.java @@ -1,6 +1,6 @@ package com.ruoyi.gateway.exception; -import lombok.NoArgsConstructor; +import com.ruoyi.gateway.constant.ErrorMessageConstant; /** * 网关异常-AK必要 @@ -8,7 +8,10 @@ import lombok.NoArgsConstructor; * @author Wenchao Gong * @date 2021-10-04 */ -@NoArgsConstructor public class AkRequireException extends GatewayException { + public AkRequireException() + { + super(ErrorMessageConstant.AK_REQUIRE); + } } diff --git a/ruoyi-extend/ruoyi-isc-gateway/src/main/java/com/ruoyi/gateway/exception/ContentTypeNotSupportedException.java b/ruoyi-extend/ruoyi-isc-gateway/src/main/java/com/ruoyi/gateway/exception/ContentTypeNotSupportedException.java index b5a5c9e2d..892343534 100644 --- a/ruoyi-extend/ruoyi-isc-gateway/src/main/java/com/ruoyi/gateway/exception/ContentTypeNotSupportedException.java +++ b/ruoyi-extend/ruoyi-isc-gateway/src/main/java/com/ruoyi/gateway/exception/ContentTypeNotSupportedException.java @@ -1,6 +1,6 @@ package com.ruoyi.gateway.exception; -import lombok.NoArgsConstructor; +import com.ruoyi.gateway.constant.ErrorMessageConstant; /** * 网关异常-请求方法不支持 @@ -8,7 +8,10 @@ import lombok.NoArgsConstructor; * @author Wenchao Gong * @date 2021-10-04 */ -@NoArgsConstructor public class ContentTypeNotSupportedException extends GatewayException { + public ContentTypeNotSupportedException() + { + super(ErrorMessageConstant.CONTENT_TYPE_NOT_SUPPORTED); + } } diff --git a/ruoyi-extend/ruoyi-isc-gateway/src/main/java/com/ruoyi/gateway/exception/GatewayException.java b/ruoyi-extend/ruoyi-isc-gateway/src/main/java/com/ruoyi/gateway/exception/GatewayException.java index 5b1311de5..cd7f79a42 100644 --- a/ruoyi-extend/ruoyi-isc-gateway/src/main/java/com/ruoyi/gateway/exception/GatewayException.java +++ b/ruoyi-extend/ruoyi-isc-gateway/src/main/java/com/ruoyi/gateway/exception/GatewayException.java @@ -1,6 +1,7 @@ package com.ruoyi.gateway.exception; -import lombok.NoArgsConstructor; +import org.springframework.http.HttpStatus; +import org.springframework.web.server.ResponseStatusException; /** * 网关异常 @@ -8,7 +9,30 @@ import lombok.NoArgsConstructor; * @author Wenchao Gong * @date 2021-10-04 */ -@NoArgsConstructor -public class GatewayException extends RuntimeException { +public class GatewayException extends ResponseStatusException { + public GatewayException(String reason) + { + super(HttpStatus.INTERNAL_SERVER_ERROR, reason); + } + + public GatewayException(HttpStatus status) + { + super(status); + } + + public GatewayException(HttpStatus status, String reason) + { + super(status, reason); + } + + public GatewayException(HttpStatus status, String reason, Throwable cause) + { + super(status, reason, cause); + } + + public GatewayException(int rawStatusCode, String reason, Throwable cause) + { + super(rawStatusCode, reason, cause); + } } diff --git a/ruoyi-extend/ruoyi-isc-gateway/src/main/java/com/ruoyi/gateway/exception/MethodNotSupportedException.java b/ruoyi-extend/ruoyi-isc-gateway/src/main/java/com/ruoyi/gateway/exception/MethodNotSupportedException.java index 42783bc10..8f465a784 100644 --- a/ruoyi-extend/ruoyi-isc-gateway/src/main/java/com/ruoyi/gateway/exception/MethodNotSupportedException.java +++ b/ruoyi-extend/ruoyi-isc-gateway/src/main/java/com/ruoyi/gateway/exception/MethodNotSupportedException.java @@ -1,6 +1,6 @@ package com.ruoyi.gateway.exception; -import lombok.NoArgsConstructor; +import com.ruoyi.gateway.constant.ErrorMessageConstant; /** * 网关异常-请求方法不支持 @@ -8,7 +8,10 @@ import lombok.NoArgsConstructor; * @author Wenchao Gong * @date 2021-10-04 */ -@NoArgsConstructor public class MethodNotSupportedException extends GatewayException { + public MethodNotSupportedException() + { + super(ErrorMessageConstant.METHOD_NOT_SUPPORTED); + } } diff --git a/ruoyi-extend/ruoyi-isc-gateway/src/main/java/com/ruoyi/gateway/exception/RateLimitException.java b/ruoyi-extend/ruoyi-isc-gateway/src/main/java/com/ruoyi/gateway/exception/RateLimitException.java index cf3dbb349..7b6b3b107 100644 --- a/ruoyi-extend/ruoyi-isc-gateway/src/main/java/com/ruoyi/gateway/exception/RateLimitException.java +++ b/ruoyi-extend/ruoyi-isc-gateway/src/main/java/com/ruoyi/gateway/exception/RateLimitException.java @@ -1,9 +1,7 @@ package com.ruoyi.gateway.exception; -import cn.hutool.core.util.StrUtil; -import lombok.NoArgsConstructor; - -import java.util.concurrent.TimeUnit; +import com.ruoyi.gateway.constant.ErrorMessageConstant; +import org.springframework.http.HttpStatus; /** * 网关异常-限流异常 @@ -11,18 +9,10 @@ import java.util.concurrent.TimeUnit; * @author Wenchao Gong * @date 2021-10-04 */ -@NoArgsConstructor public class RateLimitException extends GatewayException { - private TimeUnit timeUnit; - private int replenishRate; - private int tokensLeft; - private String message; - public RateLimitException(int replenishRate, int tokensLeft, TimeUnit timeUnit) { - this.replenishRate = replenishRate; - this.tokensLeft = tokensLeft; - this.timeUnit = timeUnit; - String unit = StrUtil.upperFirst(timeUnit.name().toLowerCase()); - message = String.format("服务限流: 上限[%d/%s], 实际[%d/%s]", replenishRate, unit, tokensLeft, unit); + public RateLimitException() + { + super(HttpStatus.TOO_MANY_REQUESTS, ErrorMessageConstant.RATE_LIMIT); } } diff --git a/ruoyi-extend/ruoyi-isc-gateway/src/main/java/com/ruoyi/gateway/exception/RuleExpiredException.java b/ruoyi-extend/ruoyi-isc-gateway/src/main/java/com/ruoyi/gateway/exception/RuleExpiredException.java index e3d48deea..7b678f85b 100644 --- a/ruoyi-extend/ruoyi-isc-gateway/src/main/java/com/ruoyi/gateway/exception/RuleExpiredException.java +++ b/ruoyi-extend/ruoyi-isc-gateway/src/main/java/com/ruoyi/gateway/exception/RuleExpiredException.java @@ -1,6 +1,6 @@ package com.ruoyi.gateway.exception; -import lombok.NoArgsConstructor; +import com.ruoyi.gateway.constant.ErrorMessageConstant; /** * 网关异常-规则已过期 @@ -8,7 +8,10 @@ import lombok.NoArgsConstructor; * @author Wenchao Gong * @date 2021-10-04 */ -@NoArgsConstructor public class RuleExpiredException extends GatewayException { + public RuleExpiredException() + { + super(ErrorMessageConstant.RULE_EXPIRED); + } } diff --git a/ruoyi-extend/ruoyi-isc-gateway/src/main/java/com/ruoyi/gateway/exception/RuleNotExistException.java b/ruoyi-extend/ruoyi-isc-gateway/src/main/java/com/ruoyi/gateway/exception/RuleNotExistException.java index 634d9d4e1..62519a323 100644 --- a/ruoyi-extend/ruoyi-isc-gateway/src/main/java/com/ruoyi/gateway/exception/RuleNotExistException.java +++ b/ruoyi-extend/ruoyi-isc-gateway/src/main/java/com/ruoyi/gateway/exception/RuleNotExistException.java @@ -1,6 +1,6 @@ package com.ruoyi.gateway.exception; -import lombok.NoArgsConstructor; +import com.ruoyi.gateway.constant.ErrorMessageConstant; /** * 网关异常-规则不存在 @@ -8,7 +8,10 @@ import lombok.NoArgsConstructor; * @author Wenchao Gong * @date 2021-10-04 */ -@NoArgsConstructor public class RuleNotExistException extends GatewayException { + public RuleNotExistException() + { + super(ErrorMessageConstant.RULE_NOT_EXIST); + } } diff --git a/ruoyi-extend/ruoyi-isc-gateway/src/main/java/com/ruoyi/gateway/filter/CustomerGlobalFilter.java b/ruoyi-extend/ruoyi-isc-gateway/src/main/java/com/ruoyi/gateway/filter/CustomerGlobalFilter.java index f1ab64976..a381bd219 100644 --- a/ruoyi-extend/ruoyi-isc-gateway/src/main/java/com/ruoyi/gateway/filter/CustomerGlobalFilter.java +++ b/ruoyi-extend/ruoyi-isc-gateway/src/main/java/com/ruoyi/gateway/filter/CustomerGlobalFilter.java @@ -16,7 +16,6 @@ import org.springframework.cloud.gateway.support.ServerWebExchangeUtils; import org.springframework.core.Ordered; import org.springframework.http.HttpHeaders; import org.springframework.http.HttpMethod; -import org.springframework.http.HttpStatus; import org.springframework.http.MediaType; import org.springframework.http.server.reactive.ServerHttpRequest; import org.springframework.util.LinkedMultiValueMap; @@ -34,7 +33,6 @@ import java.util.function.BiConsumer; import java.util.function.Supplier; import java.util.stream.Collectors; -import static org.springframework.cloud.gateway.support.ServerWebExchangeUtils.setResponseStatus; import static org.springframework.util.CollectionUtils.unmodifiableMultiValueMap; /** @@ -310,9 +308,7 @@ public class CustomerGlobalFilter implements GlobalFilter, Ordered { } return rateLimiter(exchange, rule, route, index + 1, rateLimiterAfterSupplier); } - setResponseStatus(exchange, HttpStatus.TOO_MANY_REQUESTS); - exchange.getResponse().setComplete(); - return Mono.error(new RateLimitException(limit.intValue(), 1, timeUnit)); + return Mono.error(RateLimitException::new); }); } }