詳解SpringCloud Finchley Gateway 統(tǒng)一異常處理
SpringCloud Finchley Gateway 統(tǒng)一異常處理
全文搜索[@@]搜索重點(diǎn)內(nèi)容標(biāo)記
1 . 問(wèn)題:使用SpringCloud Gateway時(shí),會(huì)出現(xiàn)各種系統(tǒng)級(jí)異常,默認(rèn)返回HTML.
2 . Finchley版本的Gateway,使用WebFlux形式作為底層框架,而不是Servlet容器,所以常規(guī)的異常處理無(wú)法使用
翻閱源碼,默認(rèn)是使用DefaultErrorWebExceptionHandler
這個(gè)類實(shí)現(xiàn)結(jié)構(gòu)如下:
可以實(shí)現(xiàn)參考DefaultErrorWebExceptionHandler
和AbstractErrorWebExceptionHandler
自定義實(shí)現(xiàn)ErrorWebExceptionHandler
,然后,注冊(cè)為Bean到Spring容器中即可(Bean Name:"errorWebExceptionHandler"
)
具體實(shí)現(xiàn)代碼如下:
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: 統(tǒng)一異常處理,參考{@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(); /** * 存儲(chǔ)處理異常后的信息 */ 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) { /** * 按照異常類型進(jìn)行處理 */ 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"; } /** * 封裝響應(yīng)體,此body可修改為自己的jsonBody */ Map<String,Object> result = new HashMap<>(2,1); result.put("httpStatus",httpStatus); result.put("body",body); /** * 錯(cuò)誤記錄 */ ServerHttpRequest request = exchange.getRequest(); log.error("[全局異常處理]異常請(qǐng)求路徑:{},記錄異常信息:{}",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; } } }
注冊(cè)Bean
/** * 自定義異常處理[@@]注冊(cè)Bean時(shí)依賴的Bean,會(huì)從容器中直接獲取,所以直接注入即可 * @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; }
[@@]注意事項(xiàng):
1 .上面為示例代碼,其中牽扯到策略工廠和響應(yīng)封裝的類,可以自定義實(shí)現(xiàn)
2 .注冊(cè)Bean時(shí)依賴的Bean,都會(huì)從Spring容器中獲取到
3 .參考此方法思路,可實(shí)現(xiàn)統(tǒng)一異常處理,統(tǒng)一封裝錯(cuò)誤信息。
以上就是本文的全部?jī)?nèi)容,希望對(duì)大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。
相關(guān)文章
Maven一鍵部署Springboot到Docker倉(cāng)庫(kù)為自動(dòng)化做準(zhǔn)備(推薦)
這篇文章主要介紹了Maven一鍵部署Springboot到Docker倉(cāng)庫(kù),本文給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2020-07-07MyBatis-Plus實(shí)現(xiàn)條件查詢的三種格式例舉詳解
本文主要介紹了MyBatis-Plus三中條件查詢格式的示例,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2022-08-08Spring Boot的應(yīng)用啟動(dòng)與關(guān)閉的方法
本篇文章主要介紹了Spring Boot的應(yīng)用啟動(dòng)與關(guān)閉的方法,小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2017-12-12解決cmd執(zhí)行javac報(bào)錯(cuò):不是內(nèi)部或外部命令,也不是可運(yùn)行的程序
剛接觸JAVA的新手可能就不知道怎么解決'JAVAC'不是內(nèi)部命令或外部命令,這篇文章主要給大家介紹了關(guān)于解決cmd執(zhí)行javac報(bào)錯(cuò):不是內(nèi)部或外部命令,也不是可運(yùn)行的程序的相關(guān)資料,需要的朋友可以參考下2023-11-11java 中mongodb的各種操作查詢的實(shí)例詳解
這篇文章主要介紹了java 中mongodb的各種操作查詢的實(shí)例詳解的相關(guān)資料,希望通過(guò)本文能幫助到大家,需要的朋友可以參考下2017-09-09java數(shù)組復(fù)制的四種方法效率對(duì)比
這篇文章主要介紹了java數(shù)組復(fù)制的四種方法效率對(duì)比,文中有簡(jiǎn)單的代碼示例,以及效率的比較結(jié)果,具有一定參考價(jià)值,需要的朋友可以了解下。2017-11-11Java遍歷輸出指定目錄、樹(shù)形結(jié)構(gòu)所有文件包括子目錄下的文件
這篇文章主要介紹了Java遍歷輸出指定目錄、樹(shù)形結(jié)構(gòu)下的所有文件包括子目錄中的文件,需要的朋友可以參考下2015-07-07java?數(shù)組實(shí)現(xiàn)學(xué)生成績(jī)統(tǒng)計(jì)教程
這篇文章主要介紹了java?數(shù)組實(shí)現(xiàn)學(xué)生成績(jī)統(tǒng)計(jì)教程,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2021-12-12