diff --git a/yudao-gateway/src/main/java/cn/iocoder/yudao/gateway/handler/GlobalExceptionHandler.java b/yudao-gateway/src/main/java/cn/iocoder/yudao/gateway/handler/GlobalExceptionHandler.java new file mode 100644 index 000000000..51ca11773 --- /dev/null +++ b/yudao-gateway/src/main/java/cn/iocoder/yudao/gateway/handler/GlobalExceptionHandler.java @@ -0,0 +1,74 @@ +package cn.iocoder.yudao.gateway.handler; + +import cn.iocoder.yudao.framework.common.pojo.CommonResult; +import cn.iocoder.yudao.gateway.util.WebFrameworkUtils; +import lombok.extern.slf4j.Slf4j; +import org.springframework.boot.web.reactive.error.ErrorWebExceptionHandler; +import org.springframework.core.annotation.Order; +import org.springframework.http.server.reactive.ServerHttpRequest; +import org.springframework.http.server.reactive.ServerHttpResponse; +import org.springframework.stereotype.Component; +import org.springframework.web.bind.annotation.ExceptionHandler; +import org.springframework.web.server.ResponseStatusException; +import org.springframework.web.server.ServerWebExchange; +import reactor.core.publisher.Mono; + +import static cn.iocoder.yudao.framework.common.exception.enums.GlobalErrorCodeConstants.INTERNAL_SERVER_ERROR; + +/** + * Gateway 的全局异常处理器,将 Exception 翻译成 CommonResult + 对应的异常编号 + * + * 在功能上,和 yudao-spring-boot-starter-web 的 GlobalExceptionHandler 类是一致的 + * + * @author 芋道源码 + */ +@Component +@Order(-1) // 保证优先级高于默认的 Spring Cloud Gateway 的 ErrorWebExceptionHandler 实现 +@Slf4j +public class GlobalExceptionHandler implements ErrorWebExceptionHandler { + + @Override + public Mono handle(ServerWebExchange exchange, Throwable ex) { + // 已经 commit,则直接返回异常 + ServerHttpResponse response = exchange.getResponse(); + if (response.isCommitted()) { + return Mono.error(ex); + } + + // 转换成 CommonResult + CommonResult result; + if (ex instanceof ResponseStatusException) { + result = responseStatusExceptionHandler(exchange, (ResponseStatusException) ex); + } else { + result = defaultExceptionHandler(exchange, ex); + } + + // 返回给前端 + return WebFrameworkUtils.writeJSON(exchange, result); + } + + /** + * 处理 Spring Cloud Gateway 默认抛出的 ResponseStatusException 异常 + */ + private CommonResult responseStatusExceptionHandler(ServerWebExchange exchange, + ResponseStatusException ex) { + // TODO 芋艿:这里要精细化翻译,默认返回用户是看不懂的 + ServerHttpRequest request = exchange.getRequest(); + log.error("[responseStatusExceptionHandler][uri({}/{}) 发生异常]", request.getURI(), request.getMethod(), ex); + return CommonResult.error(ex.getRawStatusCode(), ex.getReason()); + } + + /** + * 处理系统异常,兜底处理所有的一切 + */ + @ExceptionHandler(value = Exception.class) + public CommonResult defaultExceptionHandler(ServerWebExchange exchange, + Throwable ex) { + ServerHttpRequest request = exchange.getRequest(); + log.error("[defaultExceptionHandler][uri({}/{}) 发生异常]", request.getURI(), request.getMethod(), ex); + // TODO 芋艿:是否要插入异常日志呢? + // 返回 ERROR CommonResult + return CommonResult.error(INTERNAL_SERVER_ERROR.getCode(), INTERNAL_SERVER_ERROR.getMsg()); + } + +} diff --git a/yudao-gateway/src/main/java/cn/iocoder/yudao/gateway/util/WebFrameworkUtils.java b/yudao-gateway/src/main/java/cn/iocoder/yudao/gateway/util/WebFrameworkUtils.java index e696ae710..e056c32df 100644 --- a/yudao-gateway/src/main/java/cn/iocoder/yudao/gateway/util/WebFrameworkUtils.java +++ b/yudao-gateway/src/main/java/cn/iocoder/yudao/gateway/util/WebFrameworkUtils.java @@ -2,10 +2,18 @@ package cn.iocoder.yudao.gateway.util; import cn.hutool.core.map.MapUtil; import cn.hutool.core.util.StrUtil; +import cn.hutool.extra.servlet.ServletUtil; +import cn.iocoder.yudao.framework.common.util.json.JsonUtils; import cn.iocoder.yudao.module.system.api.oauth2.dto.OAuth2AccessTokenCheckRespDTO; +import com.fasterxml.jackson.core.JsonProcessingException; +import lombok.extern.slf4j.Slf4j; +import org.springframework.core.io.buffer.DataBufferFactory; import org.springframework.http.HttpHeaders; +import org.springframework.http.MediaType; import org.springframework.http.server.reactive.ServerHttpRequest; +import org.springframework.http.server.reactive.ServerHttpResponse; import org.springframework.web.server.ServerWebExchange; +import reactor.core.publisher.Mono; import java.util.HashMap; import java.util.Map; @@ -17,6 +25,7 @@ import java.util.Map; * * @author 芋道源码 */ +@Slf4j public class WebFrameworkUtils { @SuppressWarnings("UastIncorrectHttpHeaderInspection") @@ -38,4 +47,28 @@ public class WebFrameworkUtils { httpHeaders.set(HEADER_TENANT_ID, tenantId); } + /** + * 返回 JSON 字符串 + * + * @param exchange 响应 + * @param object 对象,会序列化成 JSON 字符串 + */ + @SuppressWarnings("deprecation") // 必须使用 APPLICATION_JSON_UTF8_VALUE,否则会乱码 + public static Mono writeJSON(ServerWebExchange exchange, Object object) { + // 设置 header + ServerHttpResponse response = exchange.getResponse(); + response.getHeaders().setContentType(MediaType.APPLICATION_JSON_UTF8); + // 设置 body + return response.writeWith(Mono.fromSupplier(() -> { + DataBufferFactory bufferFactory = response.bufferFactory(); + try { + return bufferFactory.wrap(JsonUtils.toJsonByte(object)); + } catch (Exception ex) { + ServerHttpRequest request = exchange.getRequest(); + log.error("[writeJSON][uri({}/{}) 发生异常]", request.getURI(), request.getMethod(), ex); + return bufferFactory.wrap(new byte[0]); + } + })); + } + } diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/errorcode/ErrorCodeController.http b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/errorcode/ErrorCodeController.http index 06b872318..40dfcccd7 100644 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/errorcode/ErrorCodeController.http +++ b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/errorcode/ErrorCodeController.http @@ -1,5 +1,5 @@ ### 创建错误码 -POST {{baseUrl}}/inra/error-code/create +POST {{systemBaseUrl}}/system/error-code/create Authorization: Bearer {{token}} Content-Type: application/json tenant-id: {{adminTenentId}}