使用网关时,往往会出现一些异常情况,导致没办法请求到真实的URL路径,所以在发生异常时,最好使用一个统一异常处理,返回一些友好的信息。
我使用的是SpringCloud-Gateway网关
统一异常处理步骤:
- 添加异常配置类
java
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<List<ViewResolver>> viewResolversProvider,
ServerCodecConfigurer serverCodecConfigurer,
CustomWebExceptionHandler customWebExceptionHandler) {
customWebExceptionHandler.setViewResolvers(viewResolversProvider.getIfAvailable(Collections::emptyList));
customWebExceptionHandler.setMessageWriters(serverCodecConfigurer.getWriters());
customWebExceptionHandler.setMessageReaders(serverCodecConfigurer.getReaders());
return customWebExceptionHandler;
}
}
- 编写异常处理器
java
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<HttpMessageReader<?>> messageReaders = Collections.emptyList();
/**
* MessageWriter
*/
private List<HttpMessageWriter<?>> messageWriters = Collections.emptyList();
/**
* ViewResolvers
*/
private List<ViewResolver> viewResolvers = Collections.emptyList();
/**
* 存储处理异常后的信息
*/
private ThreadLocal<ApiResult> exceptionHandlerResult = new ThreadLocal<>();
/**
* 参考AbstractErrorWebExceptionHandler
*/
public void setMessageReaders(List<HttpMessageReader<?>> messageReaders) {
Assert.notNull(messageReaders, "'messageReaders' must not be null");
this.messageReaders = messageReaders;
}
/**
* 参考AbstractErrorWebExceptionHandler
*/
public void setViewResolvers(List<ViewResolver> viewResolvers) {
this.viewResolvers = viewResolvers;
}
/**
* 参考AbstractErrorWebExceptionHandler
*/
public void setMessageWriters(List<HttpMessageWriter<?>> messageWriters) {
Assert.notNull(messageWriters, "'messageWriters' must not be null");
this.messageWriters = messageWriters;
}
@Override
public Mono<Void> 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<ServerResponse> 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<HttpMessageWriter<?>> messageWriters() {
return CustomWebExceptionHandler.this.messageWriters;
}
@Override
public List<ViewResolver> viewResolvers() {
return CustomWebExceptionHandler.this.viewResolvers;
}
}
}
- 定义返回数据格式
java
import org.springframework.http.HttpStatus;
import java.io.Serializable;
public class ApiResult<T> 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 +
'}';
}
}