异常全局处理

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; package com.ruoyi.gateway.config;
import com.ruoyi.gateway.config.handler.GlobalErrorWebExceptionHandler;
import com.ruoyi.gateway.config.provider.RedisRouteDefinitionRepository; import com.ruoyi.gateway.config.provider.RedisRouteDefinitionRepository;
import com.ruoyi.gateway.filter.CustomerGlobalFilter; import com.ruoyi.gateway.filter.CustomerGlobalFilter;
import com.ruoyi.gateway.ratelimit.CustomerRedisRateLimiter; import com.ruoyi.gateway.ratelimit.CustomerRedisRateLimiter;
import org.redisson.api.RedissonClient; import org.redisson.api.RedissonClient;
import org.springframework.beans.factory.annotation.Qualifier; 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.GlobalFilter;
import org.springframework.cloud.gateway.filter.ratelimit.RedisRateLimiter; import org.springframework.cloud.gateway.filter.ratelimit.RedisRateLimiter;
import org.springframework.cloud.gateway.route.RouteDefinitionRepository; import org.springframework.cloud.gateway.route.RouteDefinitionRepository;
@ -53,4 +55,9 @@ public class GatewayConfig
{ {
return new CustomerGlobalFilter(customerRedisRateLimiter); 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; package com.ruoyi.gateway.exception;
import lombok.NoArgsConstructor; import com.ruoyi.gateway.constant.ErrorMessageConstant;
/** /**
* 网关异常-AK必要 * 网关异常-AK必要
@ -8,7 +8,10 @@ import lombok.NoArgsConstructor;
* @author Wenchao Gong * @author Wenchao Gong
* @date 2021-10-04 * @date 2021-10-04
*/ */
@NoArgsConstructor
public class AkRequireException extends GatewayException { public class AkRequireException extends GatewayException {
public AkRequireException()
{
super(ErrorMessageConstant.AK_REQUIRE);
}
} }

View File

@ -1,6 +1,6 @@
package com.ruoyi.gateway.exception; package com.ruoyi.gateway.exception;
import lombok.NoArgsConstructor; import com.ruoyi.gateway.constant.ErrorMessageConstant;
/** /**
* 网关异常-请求方法不支持 * 网关异常-请求方法不支持
@ -8,7 +8,10 @@ import lombok.NoArgsConstructor;
* @author Wenchao Gong * @author Wenchao Gong
* @date 2021-10-04 * @date 2021-10-04
*/ */
@NoArgsConstructor
public class ContentTypeNotSupportedException extends GatewayException { public class ContentTypeNotSupportedException extends GatewayException {
public ContentTypeNotSupportedException()
{
super(ErrorMessageConstant.CONTENT_TYPE_NOT_SUPPORTED);
}
} }

View File

@ -1,6 +1,7 @@
package com.ruoyi.gateway.exception; 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 * @author Wenchao Gong
* @date 2021-10-04 * @date 2021-10-04
*/ */
@NoArgsConstructor public class GatewayException extends ResponseStatusException {
public class GatewayException extends RuntimeException {
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; package com.ruoyi.gateway.exception;
import lombok.NoArgsConstructor; import com.ruoyi.gateway.constant.ErrorMessageConstant;
/** /**
* 网关异常-请求方法不支持 * 网关异常-请求方法不支持
@ -8,7 +8,10 @@ import lombok.NoArgsConstructor;
* @author Wenchao Gong * @author Wenchao Gong
* @date 2021-10-04 * @date 2021-10-04
*/ */
@NoArgsConstructor
public class MethodNotSupportedException extends GatewayException { public class MethodNotSupportedException extends GatewayException {
public MethodNotSupportedException()
{
super(ErrorMessageConstant.METHOD_NOT_SUPPORTED);
}
} }

View File

@ -1,9 +1,7 @@
package com.ruoyi.gateway.exception; package com.ruoyi.gateway.exception;
import cn.hutool.core.util.StrUtil; import com.ruoyi.gateway.constant.ErrorMessageConstant;
import lombok.NoArgsConstructor; import org.springframework.http.HttpStatus;
import java.util.concurrent.TimeUnit;
/** /**
* 网关异常-限流异常 * 网关异常-限流异常
@ -11,18 +9,10 @@ import java.util.concurrent.TimeUnit;
* @author Wenchao Gong * @author Wenchao Gong
* @date 2021-10-04 * @date 2021-10-04
*/ */
@NoArgsConstructor
public class RateLimitException extends GatewayException { public class RateLimitException extends GatewayException {
private TimeUnit timeUnit; public RateLimitException()
private int replenishRate; {
private int tokensLeft; super(HttpStatus.TOO_MANY_REQUESTS, ErrorMessageConstant.RATE_LIMIT);
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);
} }
} }

View File

@ -1,6 +1,6 @@
package com.ruoyi.gateway.exception; package com.ruoyi.gateway.exception;
import lombok.NoArgsConstructor; import com.ruoyi.gateway.constant.ErrorMessageConstant;
/** /**
* 网关异常-规则已过期 * 网关异常-规则已过期
@ -8,7 +8,10 @@ import lombok.NoArgsConstructor;
* @author Wenchao Gong * @author Wenchao Gong
* @date 2021-10-04 * @date 2021-10-04
*/ */
@NoArgsConstructor
public class RuleExpiredException extends GatewayException { public class RuleExpiredException extends GatewayException {
public RuleExpiredException()
{
super(ErrorMessageConstant.RULE_EXPIRED);
}
} }

View File

@ -1,6 +1,6 @@
package com.ruoyi.gateway.exception; package com.ruoyi.gateway.exception;
import lombok.NoArgsConstructor; import com.ruoyi.gateway.constant.ErrorMessageConstant;
/** /**
* 网关异常-规则不存在 * 网关异常-规则不存在
@ -8,7 +8,10 @@ import lombok.NoArgsConstructor;
* @author Wenchao Gong * @author Wenchao Gong
* @date 2021-10-04 * @date 2021-10-04
*/ */
@NoArgsConstructor
public class RuleNotExistException extends GatewayException { 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.core.Ordered;
import org.springframework.http.HttpHeaders; import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod; import org.springframework.http.HttpMethod;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType; import org.springframework.http.MediaType;
import org.springframework.http.server.reactive.ServerHttpRequest; import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.util.LinkedMultiValueMap; import org.springframework.util.LinkedMultiValueMap;
@ -34,7 +33,6 @@ import java.util.function.BiConsumer;
import java.util.function.Supplier; import java.util.function.Supplier;
import java.util.stream.Collectors; import java.util.stream.Collectors;
import static org.springframework.cloud.gateway.support.ServerWebExchangeUtils.setResponseStatus;
import static org.springframework.util.CollectionUtils.unmodifiableMultiValueMap; 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); return rateLimiter(exchange, rule, route, index + 1, rateLimiterAfterSupplier);
} }
setResponseStatus(exchange, HttpStatus.TOO_MANY_REQUESTS); return Mono.error(RateLimitException::new);
exchange.getResponse().setComplete();
return Mono.error(new RateLimitException(limit.intValue(), 1, timeUnit));
}); });
} }
} }