首页 > 编程 > Java > 正文

详解SpringCloud Finchley Gateway 统一异常处理

2019-11-26 09:33:48
字体:
来源:转载
供稿:网友

SpringCloud Finchley Gateway 统一异常处理

全文搜索[@@]搜索重点内容标记

1 . 问题:使用SpringCloud Gateway时,会出现各种系统级异常,默认返回HTML.

2 . Finchley版本的Gateway,使用WebFlux形式作为底层框架,而不是Servlet容器,所以常规的异常处理无法使用

翻阅源码,默认是使用DefaultErrorWebExceptionHandler这个类实现结构如下:

可以实现参考DefaultErrorWebExceptionHandlerAbstractErrorWebExceptionHandler自定义实现ErrorWebExceptionHandler,然后,注册为Bean到Spring容器中即可(Bean Name:"errorWebExceptionHandler"

具体实现代码如下:

package pro.chenggang.example.spring.cloud.gateway.support;import org.slf4j.Logger;import org.slf4j.LoggerFactory;import org.springframework.boot.web.reactive.error.ErrorWebExceptionHandler;import org.springframework.cloud.gateway.support.NotFoundException;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.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.HashMap;import java.util.List;import java.util.Map;/** * @classDesc: 统一异常处理,参考{@link org.springframework.web.server.AbstractErrorWebExceptionHandler}修改 * @author: chenggang * @createTime: 2018/10/30 */public class JsonExceptionHandler implements ErrorWebExceptionHandler { private static final Logger log = LoggerFactory.getLogger(JsonExceptionHandler.class); /**  * MessageReader  */ private List<HttpMessageReader<?>> messageReaders = Collections.emptyList(); /**  * MessageWriter  */ private List<HttpMessageWriter<?>> messageWriters = Collections.emptyList(); /**  * ViewResolvers  */ private List<ViewResolver> viewResolvers = Collections.emptyList(); /**  * 存储处理异常后的信息  */ private ThreadLocal<Map<String,Object>> exceptionHandlerResult = new ThreadLocal<>(); /**  * 参考AbstractErrorWebExceptionHandler  * @param messageReaders  */ public void setMessageReaders(List<HttpMessageReader<?>> messageReaders) {  Assert.notNull(messageReaders, "'messageReaders' must not be null");  this.messageReaders = messageReaders; } /**  * 参考AbstractErrorWebExceptionHandler  * @param viewResolvers  */ public void setViewResolvers(List<ViewResolver> viewResolvers) {  this.viewResolvers = viewResolvers; } /**  * 参考AbstractErrorWebExceptionHandler  * @param messageWriters  */ 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;  if (ex instanceof NotFoundException) {   httpStatus = HttpStatus.NOT_FOUND;   body = "Service Not Found";  }else if(ex instanceof ResponseStatusException) {   ResponseStatusException responseStatusException = (ResponseStatusException) ex;   httpStatus = responseStatusException.getStatus();   body = responseStatusException.getMessage();  }else{   httpStatus = HttpStatus.INTERNAL_SERVER_ERROR;   body ="Internal Server Error";  }  /**   * 封装响应体,此body可修改为自己的jsonBody   */  Map<String,Object> result = new HashMap<>(2,1);  result.put("httpStatus",httpStatus);  result.put("body",body);  /**   * 错误记录   */  ServerHttpRequest request = exchange.getRequest();  log.error("[全局异常处理]异常请求路径:{},记录异常信息:{}",request.getPath(),ex.getMessage());  /**   * 参考AbstractErrorWebExceptionHandler   */  if (exchange.getResponse().isCommitted()) {   return Mono.error(ex);  }  exceptionHandlerResult.set(result);  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  * @param request  * @return  */ protected Mono<ServerResponse> renderErrorResponse(ServerRequest request) {  Map<String,Object> result = exceptionHandlerResult.get();  return ServerResponse.status((HttpStatus) result.get("httpStatus"))    .contentType(MediaType.APPLICATION_JSON_UTF8)    .body(BodyInserters.fromObject(result.get("body"))); } /**  * 参考AbstractErrorWebExceptionHandler  * @param exchange  * @param response  * @return  */ 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 JsonExceptionHandler.this.messageWriters;  }  @Override  public List<ViewResolver> viewResolvers() {   return JsonExceptionHandler.this.viewResolvers;  } }}

注册Bean

 /**  * 自定义异常处理[@@]注册Bean时依赖的Bean,会从容器中直接获取,所以直接注入即可  * @param viewResolversProvider  * @param serverCodecConfigurer  * @return  */ @Primary @Bean @Order(Ordered.HIGHEST_PRECEDENCE) public ErrorWebExceptionHandler errorWebExceptionHandler(ObjectProvider<List<ViewResolver>> viewResolversProvider,                ServerCodecConfigurer serverCodecConfigurer) {  JsonExceptionHandler jsonExceptionHandler = new JsonExceptionHandler();  jsonExceptionHandler.setViewResolvers(viewResolversProvider.getIfAvailable(Collections::emptyList));  jsonExceptionHandler.setMessageWriters(serverCodecConfigurer.getWriters());  jsonExceptionHandler.setMessageReaders(serverCodecConfigurer.getReaders());  log.debug("Init Json Exception Handler Instead Default ErrorWebExceptionHandler Success");  return jsonExceptionHandler; }

[@@]注意事项:

1 .上面为示例代码,其中牵扯到策略工厂和响应封装的类,可以自定义实现

2 .注册Bean时依赖的Bean,都会从Spring容器中获取到

3 .参考此方法思路,可实现统一异常处理,统一封装错误信息。

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持武林网。

发表评论 共有条评论
用户名: 密码:
验证码: 匿名发表