背景 后端提供的服务,都是需要统一格式的,比如都需要返回错误码,错误信息,全局流水等等。那么在后端系统中,如果抛了异常不处理的话,这些格式就没法控制,今天讲讲springCloudGateway和springboot服务怎么在抛异常的时候,统一格式。springCloudGateway网关 网关分为限流异常和业务异常限流异常 网关有限流的功能,那么在限流的时候,需要对返回做处理(这里用的sentinel做的限流) 首先我们有个配置类ConfigurationpublicclassSentinelException{privatefinalListViewResolverviewResolvers;privatefinalServerCodecConfigurerserverCodecConfigurer;publicSentinelException(ObjectProviderListViewResolverviewResolversProvider,ServerCodecConfigurerserverCodecConfigurer){this。viewResolversviewResolversProvider。getIfAvailable(Collections::emptyList);this。serverCodecConfigurerserverCodecConfigurer;}BeanOrder(Ordered。HIGHESTPRECEDENCE)publicSentinelGatewayBlockExceptionHandlersentinelGatewayBlockExceptionHandler(){returnnewSentinelGatewayExceptionHandler(viewResolvers,serverCodecConfigurer);}BeanOrder(Ordered。HIGHESTPRECEDENCE)publicGlobalFiltersentinelGatewayFilter(){returnnewSentinelGatewayFilter();}} 然后新建一个SentinelGatewayExceptionHandler类继承SentinelGatewayBlockExceptionHandler,重写handle方法Slf4jpublicclassSentinelGatewayExceptionHandlerextendsSentinelGatewayBlockExceptionHandler{privateListViewResolverviewResolvers;privateListHttpMessageWriterlt;?messageWriters;publicSentinelGatewayExceptionHandler(ListViewResolverviewResolvers,ServerCodecConfigurerserverCodecConfigurer){super(viewResolvers,serverCodecConfigurer);this。viewResolversviewResolvers;this。messageWritersserverCodecConfigurer。getWriters();}OverridepublicMonoVoidhandle(ServerWebExchangeserverWebExchange,Throwablethrowable){if(serverWebExchange。getResponse()。isCommitted()){returnMono。error(throwable);}if(!BlockException。isBlockException(throwable)){returnMono。error(throwable);}returnhandleBlockedRequest(serverWebExchange,throwable)。flatMap(responsewriteResponse(response,serverWebExchange));}privateMonoServerResponsehandleBlockedRequest(ServerWebExchangeexchange,Throwablethrowable){returnGatewayCallbackManager。getBlockHandler()。handleRequest(exchange,throwable);}privatefinalSupplierServerResponse。ContextcontextSupplier()newServerResponse。Context(){OverridepublicListHttpMessageWriterlt;?messageWriters(){returnSentinelGatewayExceptionHandler。this。messageWriters;}OverridepublicListViewResolverviewResolvers(){returnSentinelGatewayExceptionHandler。this。viewResolvers;}};privateMonoVoidwriteResponse(ServerResponseresponse,ServerWebExchangeexchange){MDC。clear();log。error(交易【{}】在【{}】时间被限制,请检查是否有大流量进入,exchange。getRequest()。getPath(),DateUtil。format(newDate(),CakeConstants。FORMATTIME));ServerHttpResponserespexchange。getResponse();resp。getHeaders()。add(ContentType,applicationjson;charsetUTF8);MapmapnewHashMap();StringjsonJSON。toJSONString(map,CakeConstants。FORMATTIME);DataBufferbufferresp。bufferFactory()。wrap(json。getBytes(StandardCharsets。UTF8));returnresp。writeWith(Mono。just(buffer));} MapmapnewHashMap();这里的map就是定义的返回格式,随便自己定义,返回的json数据。业务异常 网关可能发生业务异常,那么我们也要做处理。新建一个GlobalErrorWebExceptionHandler类,实现ErrorWebExceptionHandler接口,重写handle方法,这里我就不贴所有代码了。Slf4jOrder(1)ConfigurationRequiredArgsConstructor(onConstructor(Autowired))publicclassGlobalErrorWebExceptionHandlerimplementsErrorWebExceptionHandler{SneakyThrowsOverridepublicMonoVoidhandle(ServerWebExchangeexchange,Throwableex){ServerHttpResponseresponseexchange。getResponse();response。getHeaders()。setContentType(MediaType。APPLICATIONJSON);returnresponse。writeWith(Mono。fromSupplier((){DataBufferFactorybufferFactoryresponse。bufferFactory();try{returnbufferFactory。wrap(mapper。writeValueAsBytes(返回数据,自定义));}catch(Exceptione){log。info(返回失败);returnbufferFactory。wrap(JSON。toJSONBytes(返回数据,自定义));}}));}springboot服务 springboot分为流控异常,业务异常,还有404这些。流控异常 新建一个SentinelBootException类,实现BlockExceptionHandler,重写handle方法Slf4jComponentpublicclassSentinelBootExceptionimplementsBlockExceptionHandler{Overridepublicvoidhandle(HttpServletRequesthttpServletRequest,HttpServletResponsehttpServletResponse,BlockExceptione)throwsException{MDC。clear();CenterQueryResponseresponseDatanewCenterQueryResponse();httpServletResponse。setStatus(200);httpServletResponse。setContentType(applicationjson;charsetutf8);PrintWriterouthttpServletResponse。getWriter();out。print(JSON。toJSONString(responseData,CakeConstants。FORMATTIME));out。flush();out。close();}} responseData对象就是自定义返回的数据,大家按照自己项目的定义就行。404等处理 新建TransferNotFindController类,实现ErrorController接口,对error路径进行处理。RestControllerpublicclassTransferNotFindControllerimplementsErrorController{privatestaticfinalLoggerlogLoggerFactory。getLogger(TransferNotFindController。class);默认错误privatestaticfinalStringpathdefaulterror;RequestMapping(valuepathdefault,produces{MediaType。APPLICATIONJSONVALUE})publicCenterQueryResponsehandleError(HttpServletRequestrequest){CenterQueryResponseresponseDatanewCenterQueryResponse();log。info(交易不存在〔{}〕,JSON。toJSONString(responseData,CakeConstants。FORMATTIME));returnresponseData;}} CenterQueryResponse对象就是返回的数据。大家根据项目自行定义。业务异常 这部分比较麻烦,我是有对controller成做AOP切面处理的,我用了trycatch做了一些异常处理,这部分就不写了,大家可以自己去处理,在catch里面对异常处理。还有一部分是全局异常处理,利用的是ControllerAdvice注解和ExceptionHandler注解。先定义一个异常类PgException继承RuntimeException。DataNoArgsConstructorAllArgsConstructorpublicclassPgExceptionextendsRuntimeException{privateStringcode;privateStringmsg;publicPgException(Stringcode,Stringmsg){this。codecode;this。msgmsg;}OverridepublicStringgetMessage(){returnthis。msg;}OverridepublicStringtoString(){returnException(codethis。getCode(),messagethis。getMsg());}} 这里面有个地方注意一下,重写了getMessage,为什么要重写,因为在日志里你不重写的话,那么异常信息打印出来都是null,重写了,你打印的异常信息就是你的错误信息。 然后新建PgExceptionHandler类,这里的代码没完整贴出来。Slf4jControllerAdvicepublicclassPgExceptionHandler{ExceptionHandler(PgException。class)ResponseBodypublicCenterCommandResponsehandlePgException(PgExceptionex){CenterCommandResponsecenterCommandResponsenewCenterCommandResponse();RequestContext。remove();returncenterCommandResponse;}ExceptionHandler(Exception。class)ResponseBodypublicCenterCommandResponsehandleException(Exceptionex){CenterCommandResponsecenterCommandResponsenewCenterCommandResponse();returncenterCommandResponse;}} CenterCommandResponse就是要返回的数据,大家可以根据项目自行定义。 网关和服务应用的异常处理就到这了,基本包含了当前所有的异常场景了,后续还有别的再加吧。再贴个我的异常返回图。 异常返回信息