异常全局处理

This commit is contained in:
Wenchao Gong 2021-10-18 23:59:57 +08:00
parent 08c88e9e27
commit 783a3f9946
11 changed files with 156 additions and 33 deletions

View File

@ -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();
}
}

View File

@ -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<Void> 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<Integer> 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<String, Object> result(Optional<Integer> code, String message) {
final HashMap<String, Object> result = MapUtil.newHashMap(3);
result.put("code", code.orElse(HttpStatus.INTERNAL_SERVER_ERROR.value()));
result.put("message", message);
result.put("data", null);
return result;
}
}

View File

@ -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 = "规则不存在";
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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