参考 https://blog.csdn.net/suyuaidan/article/details/132663141,写法不同于注入方式不一样
ErrorWebFluxAutoConfiguration
@Configuration(proxyBeanMethods = false)
@ConditionalOnWebApplication(type = ConditionalOnWebApplication.Type.REACTIVE)
@ConditionalOnClass(WebFluxConfigurer.class)
@AutoConfigureBefore(WebFluxAutoConfiguration.class)
@EnableConfigurationProperties({ ServerProperties.class, ResourceProperties.class })
public class ErrorWebFluxAutoConfiguration {private final ServerProperties serverProperties;public ErrorWebFluxAutoConfiguration(ServerProperties serverProperties) {this.serverProperties = serverProperties;}@Bean@ConditionalOnMissingBean(value = ErrorWebExceptionHandler.class, search = SearchStrategy.CURRENT)@Order(-1)public ErrorWebExceptionHandler errorWebExceptionHandler(ErrorAttributes errorAttributes,ResourceProperties resourceProperties, ObjectProvider<ViewResolver> viewResolvers,ServerCodecConfigurer serverCodecConfigurer, ApplicationContext applicationContext) {DefaultErrorWebExceptionHandler exceptionHandler = new DefaultErrorWebExceptionHandler(errorAttributes,resourceProperties, this.serverProperties.getError(), applicationContext);exceptionHandler.setViewResolvers(viewResolvers.orderedStream().collect(Collectors.toList()));exceptionHandler.setMessageWriters(serverCodecConfigurer.getWriters());exceptionHandler.setMessageReaders(serverCodecConfigurer.getReaders());return exceptionHandler;}@Bean@ConditionalOnMissingBean(value = ErrorAttributes.class, search = SearchStrategy.CURRENT)public DefaultErrorAttributes errorAttributes() {return new DefaultErrorAttributes();}}
DefaultErrorWebExceptionHandler
public class DefaultErrorWebExceptionHandler extends AbstractErrorWebExceptionHandler {private static final MediaType TEXT_HTML_UTF8 = new MediaType("text", "html", StandardCharsets.UTF_8);private static final Map<HttpStatus.Series, String> SERIES_VIEWS;static {Map<HttpStatus.Series, String> views = new EnumMap<>(HttpStatus.Series.class);views.put(HttpStatus.Series.CLIENT_ERROR, "4xx");views.put(HttpStatus.Series.SERVER_ERROR, "5xx");SERIES_VIEWS = Collections.unmodifiableMap(views);}private final ErrorProperties errorProperties;/*** Create a new {@code DefaultErrorWebExceptionHandler} instance.* @param errorAttributes the error attributes* @param resourceProperties the resources configuration properties* @param errorProperties the error configuration properties* @param applicationContext the current application context*/public DefaultErrorWebExceptionHandler(ErrorAttributes errorAttributes, ResourceProperties resourceProperties,ErrorProperties errorProperties, ApplicationContext applicationContext) {super(errorAttributes, resourceProperties, applicationContext);this.errorProperties = errorProperties;}@Overrideprotected RouterFunction<ServerResponse> getRoutingFunction(ErrorAttributes errorAttributes) {return route(acceptsTextHtml(), this::renderErrorView).andRoute(all(), this::renderErrorResponse);}/*** Render the error information as an HTML view.* @param request the current request* @return a {@code Publisher} of the HTTP response*/protected Mono<ServerResponse> renderErrorView(ServerRequest request) {Map<String, Object> error = getErrorAttributes(request, getErrorAttributeOptions(request, MediaType.TEXT_HTML));int errorStatus = getHttpStatus(error);ServerResponse.BodyBuilder responseBody = ServerResponse.status(errorStatus).contentType(TEXT_HTML_UTF8);return Flux.just(getData(errorStatus).toArray(new String[] {})).flatMap((viewName) -> renderErrorView(viewName, responseBody, error)).switchIfEmpty(this.errorProperties.getWhitelabel().isEnabled()? renderDefaultErrorView(responseBody, error) : Mono.error(getError(request))).next();}private List<String> getData(int errorStatus) {List<String> data = new ArrayList<>();data.add("error/" + errorStatus);HttpStatus.Series series = HttpStatus.Series.resolve(errorStatus);if (series != null) {data.add("error/" + SERIES_VIEWS.get(series));}data.add("error/error");return data;}/*** Render the error information as a JSON payload.* @param request the current request* @return a {@code Publisher} of the HTTP response*/protected Mono<ServerResponse> renderErrorResponse(ServerRequest request) {Map<String, Object> error = getErrorAttributes(request, getErrorAttributeOptions(request, MediaType.ALL));return ServerResponse.status(getHttpStatus(error)).contentType(MediaType.APPLICATION_JSON).body(BodyInserters.fromValue(error));}protected ErrorAttributeOptions getErrorAttributeOptions(ServerRequest request, MediaType mediaType) {ErrorAttributeOptions options = ErrorAttributeOptions.defaults();if (this.errorProperties.isIncludeException()) {options = options.including(Include.EXCEPTION);}if (isIncludeStackTrace(request, mediaType)) {options = options.including(Include.STACK_TRACE);}if (isIncludeMessage(request, mediaType)) {options = options.including(Include.MESSAGE);}if (isIncludeBindingErrors(request, mediaType)) {options = options.including(Include.BINDING_ERRORS);}return options;}/*** Determine if the stacktrace attribute should be included.* @param request the source request* @param produces the media type produced (or {@code MediaType.ALL})* @return if the stacktrace attribute should be included*/@SuppressWarnings("deprecation")protected boolean isIncludeStackTrace(ServerRequest request, MediaType produces) {switch (this.errorProperties.getIncludeStacktrace()) {case ALWAYS:return true;case ON_PARAM:case ON_TRACE_PARAM:return isTraceEnabled(request);default:return false;}}/*** Determine if the message attribute should be included.* @param request the source request* @param produces the media type produced (or {@code MediaType.ALL})* @return if the message attribute should be included*/protected boolean isIncludeMessage(ServerRequest request, MediaType produces) {switch (this.errorProperties.getIncludeMessage()) {case ALWAYS:return true;case ON_PARAM:return isMessageEnabled(request);default:return false;}}/*** Determine if the errors attribute should be included.* @param request the source request* @param produces the media type produced (or {@code MediaType.ALL})* @return if the errors attribute should be included*/protected boolean isIncludeBindingErrors(ServerRequest request, MediaType produces) {switch (this.errorProperties.getIncludeBindingErrors()) {case ALWAYS:return true;case ON_PARAM:return isBindingErrorsEnabled(request);default:return false;}}/*** Get the HTTP error status information from the error map.* @param errorAttributes the current error information* @return the error HTTP status*/protected int getHttpStatus(Map<String, Object> errorAttributes) {return (int) errorAttributes.get("status");}/*** Predicate that checks whether the current request explicitly support* {@code "text/html"} media type.* <p>* The "match-all" media type is not considered here.* @return the request predicate*/protected RequestPredicate acceptsTextHtml() {return (serverRequest) -> {try {List<MediaType> acceptedMediaTypes = serverRequest.headers().accept();acceptedMediaTypes.removeIf(MediaType.ALL::equalsTypeAndSubtype);MediaType.sortBySpecificityAndQuality(acceptedMediaTypes);return acceptedMediaTypes.stream().anyMatch(MediaType.TEXT_HTML::isCompatibleWith);}catch (InvalidMediaTypeException ex) {return false;}};}}
观察上面的代码,我们可以知道我们要做两步工作
- 全局处理异常怎么操作(实现ErrorWebExceptionHandler,或者直接继承他的子类DefaultErrorWebExceptionHandler)
- 如何把异常处理注入到框架中(自定CustomErrorWebFluxAutoConfiguration)
自定义ErrorWebExceptionHandler
import lombok.extern.slf4j.Slf4j;
import org.springframework.boot.autoconfigure.web.ErrorProperties;
import org.springframework.boot.autoconfigure.web.ResourceProperties;
import org.springframework.boot.autoconfigure.web.reactive.error.DefaultErrorWebExceptionHandler;
import org.springframework.boot.web.reactive.error.ErrorAttributes;
import org.springframework.context.ApplicationContext;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.web.reactive.function.BodyInserters;
import org.springframework.web.reactive.function.server.*;
import reactor.core.publisher.Mono;import java.util.HashMap;
import java.util.Map;/*** 自定义异常处理器*/
@Slf4j
public class CustomErrorWebExceptionHandler extends DefaultErrorWebExceptionHandler {public CustomErrorWebExceptionHandler(ErrorAttributes errorAttributes, ResourceProperties resourceProperties,ErrorProperties errorProperties, ApplicationContext applicationContext) {super(errorAttributes, resourceProperties, errorProperties, applicationContext);}@Overrideprotected Mono<ServerResponse> renderErrorResponse(ServerRequest request) {// 原始的异常信息可以用getError方法取得Throwable throwable = getError(request);// 这里和父类的做法一样,取得DefaultErrorAttributes整理出来的所有异常信息Map<String, Object> errorAttributes = getErrorAttributes(request, getErrorAttributeOptions(request, MediaType.ALL));HttpStatus status = HttpStatus.valueOf((Integer) errorAttributes.get("status"));// todo 写你自己的逻辑也可根据status封装不同的结果集枚举Map<String, Object> responseBodyMap = new HashMap<>();responseBodyMap.put("code", "my error code");responseBodyMap.put("msg", throwable.getMessage());return ServerResponse// http返回码.status(HttpStatus.INTERNAL_SERVER_ERROR)// 类型和以前一样.contentType(MediaType.APPLICATION_JSON)// 响应body的内容.body(BodyInserters.fromValue(responseBodyMap));}@Overrideprotected RouterFunction<ServerResponse> getRoutingFunction(ErrorAttributes errorAttributes) {return RouterFunctions.route(RequestPredicates.all(), this::renderErrorResponse);}}
自定义CustomErrorWebFluxAutoConfiguration
import com.hatzi.gateway.plus.handler.CustomErrorWebExceptionHandler;
import org.springframework.beans.factory.ObjectProvider;
import org.springframework.boot.autoconfigure.AutoConfigureBefore;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication;
import org.springframework.boot.autoconfigure.condition.SearchStrategy;
import org.springframework.boot.autoconfigure.web.ResourceProperties;
import org.springframework.boot.autoconfigure.web.ServerProperties;
import org.springframework.boot.autoconfigure.web.reactive.WebFluxAutoConfiguration;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.boot.web.reactive.error.DefaultErrorAttributes;
import org.springframework.boot.web.reactive.error.ErrorAttributes;
import org.springframework.boot.web.reactive.error.ErrorWebExceptionHandler;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.Ordered;
import org.springframework.core.annotation.Order;
import org.springframework.http.codec.ServerCodecConfigurer;
import org.springframework.web.reactive.config.WebFluxConfigurer;
import org.springframework.web.reactive.result.view.ViewResolver;import java.util.List;
import java.util.stream.Collectors;/*** @Description* @Author weiwenbin* @Date 2024/6/6 15:01*/
@Configuration
@ConditionalOnWebApplication(type = ConditionalOnWebApplication.Type.REACTIVE)
@ConditionalOnClass(WebFluxConfigurer.class)
@AutoConfigureBefore(WebFluxAutoConfiguration.class)
@EnableConfigurationProperties({ServerProperties.class, ResourceProperties.class})
public class CustomErrorWebFluxAutoConfiguration {private final ServerProperties serverProperties;private final ApplicationContext applicationContext;private final ResourceProperties resourceProperties;private final List<ViewResolver> viewResolvers;private final ServerCodecConfigurer serverCodecConfigurer;public CustomErrorWebFluxAutoConfiguration(ServerProperties serverProperties,ResourceProperties resourceProperties,ObjectProvider<ViewResolver> viewResolversProvider,ServerCodecConfigurer serverCodecConfigurer,ApplicationContext applicationContext) {this.serverProperties = serverProperties;this.applicationContext = applicationContext;this.resourceProperties = resourceProperties;this.viewResolvers = viewResolversProvider.orderedStream().collect(Collectors.toList());this.serverCodecConfigurer = serverCodecConfigurer;}@Bean@ConditionalOnMissingBean(value = ErrorWebExceptionHandler.class,search = SearchStrategy.CURRENT)@Order(Ordered.HIGHEST_PRECEDENCE)public ErrorWebExceptionHandler errorWebExceptionHandler(ErrorAttributes errorAttributes) {CustomErrorWebExceptionHandler exceptionHandler = new CustomErrorWebExceptionHandler(errorAttributes,resourceProperties,this.serverProperties.getError(),applicationContext);exceptionHandler.setViewResolvers(this.viewResolvers);exceptionHandler.setMessageWriters(this.serverCodecConfigurer.getWriters());exceptionHandler.setMessageReaders(this.serverCodecConfigurer.getReaders());return exceptionHandler;}@Bean@ConditionalOnMissingBean(value = ErrorAttributes.class, search = SearchStrategy.CURRENT)public DefaultErrorAttributes errorAttributes() {return new DefaultErrorAttributes();}
}