From 5d9f5b8ab7996ad7b5057ee30cd5d760aa204214 Mon Sep 17 00:00:00 2001 From: Azure <983547216@qq.com> Date: Wed, 24 Jun 2026 12:00:17 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20HTTP=E7=8A=B6=E6=80=81=E7=A0=81=20+=20?= =?UTF-8?q?=E4=B8=9A=E5=8A=A1=E7=A0=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../handler/GlobalExceptionHandler.java | 55 +++++++++++++------ .../webgamebackend/sse/SseController.java | 2 - 2 files changed, 39 insertions(+), 18 deletions(-) diff --git a/src/main/java/com/webgame/webgamebackend/common/handler/GlobalExceptionHandler.java b/src/main/java/com/webgame/webgamebackend/common/handler/GlobalExceptionHandler.java index 7f93ffe..34d7fd0 100644 --- a/src/main/java/com/webgame/webgamebackend/common/handler/GlobalExceptionHandler.java +++ b/src/main/java/com/webgame/webgamebackend/common/handler/GlobalExceptionHandler.java @@ -4,11 +4,14 @@ import cn.dev33.satoken.exception.NotLoginException; import cn.dev33.satoken.exception.NotPermissionException; import cn.dev33.satoken.exception.NotRoleException; import cn.dev33.satoken.exception.SaTokenContextException; +import com.alibaba.fastjson2.JSON; import com.webgame.webgamebackend.common.dto.ApiResponse; import com.webgame.webgamebackend.common.exception.BusinessException; import com.webgame.webgamebackend.common.exception.ErrorCode; +import jakarta.servlet.http.HttpServletResponse; import lombok.extern.slf4j.Slf4j; import org.springframework.http.HttpStatus; +import org.springframework.http.MediaType; import org.springframework.http.ResponseEntity; import org.springframework.http.converter.HttpMessageNotReadableException; import org.springframework.web.HttpRequestMethodNotSupportedException; @@ -21,9 +24,14 @@ import org.springframework.web.method.annotation.MethodArgumentTypeMismatchExcep import org.springframework.web.multipart.MaxUploadSizeExceededException; import org.springframework.web.servlet.resource.NoResourceFoundException; +import java.io.IOException; + /** - * 全局异常处理器,返回 ResponseEntity<ApiResponse> 实现双层错误表达: + * 全局异常处理器,双层错误表达: * HTTP 状态码表达错误大类 + ApiResponse.code 表达具体业务错误码 + *
+ * 业务异常(仅来自 REST JSON 请求)返回 ResponseEntity,经过 Spring 内容协商。
+ * 框架级异常(可能来自 SSE/浏览器等任意请求类型)直接写 HttpServletResponse 绕过内容协商。
*/
@Slf4j
@RestControllerAdvice
@@ -47,7 +55,7 @@ public class GlobalExceptionHandler {
// SSE 响应已被客户端关闭,不写响应体
}
- // ==================== 业务异常 ====================
+ // ==================== 业务异常(REST JSON 请求) ====================
/**
* 业务异常 — HTTP 状态码从 ErrorCode 枚举自动派生
@@ -115,31 +123,46 @@ public class GlobalExceptionHandler {
.body(ApiResponse.fail(403, "权限不足"));
}
- // ==================== 框架级异常 ====================
+ // ==================== 框架级异常(直接写响应,绕过内容协商) ====================
+ // 这类异常可能来自任意请求类型(SSE Accept: text/event-stream、
+ // 浏览器 Accept: text/html 等),若走 ResponseEntity 会因 Accept 头
+ // 与 JSON 响应体不匹配而抛出 HttpMediaTypeNotAcceptableException。
@ExceptionHandler(NoResourceFoundException.class)
- public ResponseEntity
+ * 不依赖 Spring 的 MessageConverter 和内容协商机制,
+ * 确保 SSE/浏览器等非 JSON Accept 头的请求也能收到可读的错误信息。
+ */
+ private static void writeJsonError(HttpServletResponse response, HttpStatus status,
+ int code, String message) throws IOException {
+ response.setStatus(status.value());
+ response.setContentType(MediaType.APPLICATION_JSON_VALUE);
+ response.setCharacterEncoding("UTF-8");
+ response.getWriter().write(JSON.toJSONString(ApiResponse.fail(code, message)));
}
}
diff --git a/src/main/java/com/webgame/webgamebackend/sse/SseController.java b/src/main/java/com/webgame/webgamebackend/sse/SseController.java
index 22a4f5d..9a43f27 100644
--- a/src/main/java/com/webgame/webgamebackend/sse/SseController.java
+++ b/src/main/java/com/webgame/webgamebackend/sse/SseController.java
@@ -71,8 +71,6 @@ public class SseController {
lastEventId, userId, connectionId);
}
- log.info("[SSE] 连接建立成功, userId={}, connectionId={}, onlineConnections={}",
- userId, connectionId, connectionManager.getOnlineCount());
return emitter;
}