使用网关时,往往会出现一些异常情况,导致没办法请求到真实的URL路径,所以在发生异常时,最好使用一个统一异常处理,返回一些友好的信息。
我使用的是SpringCloud-Gateway网关
统一异常处理步骤:
1. 添加异常配置类
- import com.example.itvgetway.exception.CustomWebExceptionHandler;
- import org.springframework.beans.factory.ObjectProvider;
- import org.springframework.boot.web.reactive.error.ErrorWebExceptionHandler;
- import org.springframework.context.annotation.Bean;
- import org.springframework.context.annotation.Configuration;
- import org.springframework.context.annotation.Primary;
- import org.springframework.core.Ordered;
- import org.springframework.core.annotation.Order;
- import org.springframework.http.codec.ServerCodecConfigurer;
- import org.springframework.web.reactive.result.view.ViewResolver;
-
- import java.util.Collections;
- import java.util.List;
-
-
-
- @Configuration
- public class ExceptionConfig {
-
- /**
- * 自定义异常处理[@@]注册Bean时依赖的Bean,会从容器中直接获取,所以直接注入即可
- */
- @Primary
- @Bean
- @Order(Ordered.HIGHEST_PRECEDENCE)
- public ErrorWebExceptionHandler errorWebExceptionHandler(ObjectProvider
> viewResolversProvider,
- ServerCodecConfigurer serverCodecConfigurer,
- CustomWebExceptionHandler customWebExceptionHandler) {
- customWebExceptionHandler.setViewResolvers(viewResolversProvider.getIfAvailable(Collections::emptyList));
- customWebExceptionHandler.setMessageWriters(serverCodecConfigurer.getWriters());
- customWebExceptionHandler.setMessageReaders(serverCodecConfigurer.getReaders());
- return customWebExceptionHandler;
- }
- }
2. 编写异常处理器
- import com.example.itvgetway.util.ApiResult;
- import org.slf4j.Logger;
- import org.slf4j.LoggerFactory;
- import org.springframework.boot.web.reactive.error.ErrorWebExceptionHandler;
- import org.springframework.http.HttpStatus;
- import org.springframework.http.MediaType;
- import org.springframework.http.codec.HttpMessageReader;
- import org.springframework.http.codec.HttpMessageWriter;
- import org.springframework.http.server.reactive.ServerHttpRequest;
- import org.springframework.stereotype.Component;
- import org.springframework.util.Assert;
- import org.springframework.web.reactive.function.BodyInserters;
- import org.springframework.web.reactive.function.server.RequestPredicates;
- import org.springframework.web.reactive.function.server.RouterFunctions;
- import org.springframework.web.reactive.function.server.ServerRequest;
- import org.springframework.web.reactive.function.server.ServerResponse;
- import org.springframework.web.reactive.result.view.ViewResolver;
- import org.springframework.web.server.ResponseStatusException;
- import org.springframework.web.server.ServerWebExchange;
- import reactor.core.publisher.Mono;
-
- import java.util.Collections;
- import java.util.List;
-
-
-
- @Component
- public class CustomWebExceptionHandler implements ErrorWebExceptionHandler {
-
- private static final Logger log = LoggerFactory.getLogger(CustomWebExceptionHandler.class);
-
- /**
- * MessageReader
- */
- private List
> messageReaders = Collections.emptyList(); -
- /**
- * MessageWriter
- */
- private List
> messageWriters = Collections.emptyList(); -
- /**
- * ViewResolvers
- */
- private List
viewResolvers = Collections.emptyList(); -
- /**
- * 存储处理异常后的信息
- */
- private ThreadLocal
exceptionHandlerResult = new ThreadLocal<>(); -
- /**
- * 参考AbstractErrorWebExceptionHandler
- */
- public void setMessageReaders(List
> messageReaders) { - Assert.notNull(messageReaders, "'messageReaders' must not be null");
- this.messageReaders = messageReaders;
- }
-
- /**
- * 参考AbstractErrorWebExceptionHandler
- */
- public void setViewResolvers(List
viewResolvers) { - this.viewResolvers = viewResolvers;
- }
-
- /**
- * 参考AbstractErrorWebExceptionHandler
- */
- public void setMessageWriters(List
> messageWriters) { - Assert.notNull(messageWriters, "'messageWriters' must not be null");
- this.messageWriters = messageWriters;
- }
-
- @Override
- public Mono
handle(ServerWebExchange exchange, Throwable ex) { - // 按照异常类型进行处理
- HttpStatus httpStatus;
- String body;
- //todo 这里可以自己定义业务需要的异常
- if (ex instanceof ResponseStatusException) {
- ResponseStatusException responseStatusException = (ResponseStatusException) ex;
- httpStatus = responseStatusException.getStatus();
- body = responseStatusException.getMessage();
-
- } else {
- httpStatus = HttpStatus.INTERNAL_SERVER_ERROR;
- body = httpStatus.name();
- }
-
- ServerHttpRequest request = exchange.getRequest();
- log.error("[全局异常处理] 异常请求路径:{}, 记录异常信息:{}", request.getPath(), ex.getMessage());
- ex.printStackTrace();
-
- // 参考AbstractErrorWebExceptionHandler
- if (exchange.getResponse().isCommitted()) {
- return Mono.error(ex);
- }
-
- exceptionHandlerResult.set(ApiResult.error(httpStatus, body));
-
- ServerRequest newRequest = ServerRequest.create(exchange, this.messageReaders);
- return RouterFunctions.route(RequestPredicates.all(), this::renderErrorResponse).route(newRequest)
- .switchIfEmpty(Mono.error(ex))
- .flatMap((handler) -> handler.handle(newRequest))
- .flatMap((response) -> write(exchange, response));
-
- }
-
- /**
- * 参考DefaultErrorWebExceptionHandler
- * 在这里定义返回的是Json还是Html
- */
- protected Mono
renderErrorResponse(ServerRequest request) { - ApiResult result = exceptionHandlerResult.get();
- return ServerResponse.status(result.getCode())
- .contentType(MediaType.APPLICATION_JSON)
- .body(BodyInserters.fromValue(result));
- }
-
- /**
- * 参考AbstractErrorWebExceptionHandler
- */
- private Mono extends Void> write(ServerWebExchange exchange,
- ServerResponse response) {
- exchange.getResponse().getHeaders()
- .setContentType(response.headers().getContentType());
- return response.writeTo(exchange, new ResponseContext());
- }
-
- /**
- * 参考AbstractErrorWebExceptionHandler
- */
- private class ResponseContext implements ServerResponse.Context {
-
- @Override
- public List
> messageWriters() { - return CustomWebExceptionHandler.this.messageWriters;
- }
-
- @Override
- public List
viewResolvers() { - return CustomWebExceptionHandler.this.viewResolvers;
- }
- }
- }
3. 定义返回数据格式
- import org.springframework.http.HttpStatus;
-
- import java.io.Serializable;
-
-
- public class ApiResult
implements Serializable { -
- private static final long serialVersionUID = 1166356696537391753L;
-
- private Integer code;
-
- private String msg;
-
- private T data;
-
- public ApiResult() {
- }
-
- public ApiResult(Integer code, String msg, T data) {
- this.code = code;
- this.msg = msg;
- this.data = data;
- }
-
- public static ApiResult success() {
- return new ApiResult(HttpStatus.OK.value(), "success", null);
- }
-
- public static ApiResult success(String msg) {
- return new ApiResult(HttpStatus.OK.value(), msg, null);
- }
-
- public static ApiResult success(String msg, Object data) {
- return new ApiResult(HttpStatus.OK.value(), msg, data);
- }
-
- public static ApiResult error(HttpStatus status, String msg) {
- return new ApiResult(status.value(), msg, null);
- }
-
- public Integer getCode() {
- return code;
- }
-
- public void setCode(Integer code) {
- this.code = code;
- }
-
- public String getMsg() {
- return msg;
- }
-
- public void setMsg(String msg) {
- this.msg = msg;
- }
-
- public T getData() {
- return data;
- }
-
- public void setData(T data) {
- this.data = data;
- }
-
- @Override
- public String toString() {
- return "Result{" +
- "code=" + code +
- ", msg='" + msg + '\'' +
- ", data=" + data +
- '}';
- }
- }