fix(core): 统一全局异常处理器并添加数据库管理功能
- 在所有服务的GlobalExceptionHandler中添加HttpServletRequest参数以记录请求上下文 - 统一异常响应格式为ResponseEntity<ApiResponse<Void>>并改进错误日志记录 - 添加对多种异常类型的处理包括参数验证、请求方法不支持、权限拒绝等 - 为业务异常添加不同级别的日志记录(warn/error)和状态码映射 - 在前端系统API中新增数据库表管理相关接口定义和实现 - 添加数据库表列表、列信息和数据查询的API调用函数
这个提交包含在:
父节点
e75aa66747
当前提交
50da70d580
@ -2,11 +2,19 @@ package com.xuqm.demo.config;
|
||||
|
||||
import com.xuqm.common.exception.BusinessException;
|
||||
import com.xuqm.common.model.ApiResponse;
|
||||
import jakarta.servlet.http.HttpServletRequest;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.http.converter.HttpMessageNotReadableException;
|
||||
import org.springframework.security.authorization.AuthorizationDeniedException;
|
||||
import org.springframework.security.core.Authentication;
|
||||
import org.springframework.security.core.context.SecurityContextHolder;
|
||||
import org.springframework.web.HttpRequestMethodNotSupportedException;
|
||||
import org.springframework.web.bind.MissingServletRequestParameterException;
|
||||
import org.springframework.web.bind.MethodArgumentNotValidException;
|
||||
import org.springframework.web.bind.annotation.ExceptionHandler;
|
||||
import org.springframework.web.bind.annotation.ResponseStatus;
|
||||
import org.springframework.web.bind.annotation.RestControllerAdvice;
|
||||
import org.springframework.web.multipart.MaxUploadSizeExceededException;
|
||||
|
||||
@ -16,20 +24,84 @@ public class GlobalExceptionHandler {
|
||||
private static final Logger log = LoggerFactory.getLogger(GlobalExceptionHandler.class);
|
||||
|
||||
@ExceptionHandler(BusinessException.class)
|
||||
public ApiResponse<Void> handleBusiness(BusinessException ex) {
|
||||
return ApiResponse.error(ex.getCode(), ex.getMessage());
|
||||
public ResponseEntity<ApiResponse<Void>> handleBusiness(BusinessException ex, HttpServletRequest request) {
|
||||
if (ex.getCode() >= 500) {
|
||||
log.error("[{}] {} code={} msg={}", request.getMethod(), request.getRequestURI(), ex.getCode(), ex.getMessage(), ex);
|
||||
} else {
|
||||
log.warn("[{}] {} code={} msg={}", request.getMethod(), request.getRequestURI(), ex.getCode(), ex.getMessage());
|
||||
}
|
||||
return ResponseEntity.status(resolveStatus(ex.getCode()))
|
||||
.body(ApiResponse.error(ex.getCode(), ex.getMessage()));
|
||||
}
|
||||
|
||||
@ExceptionHandler(MaxUploadSizeExceededException.class)
|
||||
@ResponseStatus(HttpStatus.PAYLOAD_TOO_LARGE)
|
||||
public ApiResponse<Void> handleMaxUploadSize(MaxUploadSizeExceededException ex) {
|
||||
return ApiResponse.error(413, "File size exceeds the maximum allowed limit");
|
||||
public ResponseEntity<ApiResponse<Void>> handleMaxUploadSize(MaxUploadSizeExceededException ex, HttpServletRequest request) {
|
||||
log.warn("[{}] {} upload size exceeded: {}", request.getMethod(), request.getRequestURI(), ex.getMessage());
|
||||
return ResponseEntity.status(HttpStatus.PAYLOAD_TOO_LARGE)
|
||||
.body(ApiResponse.error(413, "文件大小超过限制"));
|
||||
}
|
||||
|
||||
@ExceptionHandler(MethodArgumentNotValidException.class)
|
||||
public ResponseEntity<ApiResponse<Void>> handleValidation(MethodArgumentNotValidException ex, HttpServletRequest request) {
|
||||
String message = ex.getBindingResult().getFieldErrors().stream()
|
||||
.findFirst()
|
||||
.map(error -> error.getDefaultMessage())
|
||||
.orElse("参数错误");
|
||||
log.warn("[{}] {} validation failed: {}", request.getMethod(), request.getRequestURI(), message);
|
||||
return ResponseEntity.badRequest().body(ApiResponse.badRequest(message));
|
||||
}
|
||||
|
||||
@ExceptionHandler(MissingServletRequestParameterException.class)
|
||||
public ResponseEntity<ApiResponse<Void>> handleMissingParam(MissingServletRequestParameterException ex, HttpServletRequest request) {
|
||||
log.warn("[{}] {} missing param: {}", request.getMethod(), request.getRequestURI(), ex.getParameterName());
|
||||
return ResponseEntity.badRequest()
|
||||
.body(ApiResponse.badRequest("缺少必填参数: " + ex.getParameterName()));
|
||||
}
|
||||
|
||||
@ExceptionHandler(IllegalArgumentException.class)
|
||||
public ResponseEntity<ApiResponse<Void>> handleIllegalArgument(IllegalArgumentException ex, HttpServletRequest request) {
|
||||
log.warn("[{}] {} illegal argument: {}", request.getMethod(), request.getRequestURI(), ex.getMessage());
|
||||
return ResponseEntity.badRequest().body(ApiResponse.badRequest(ex.getMessage() == null ? "参数错误" : ex.getMessage()));
|
||||
}
|
||||
|
||||
@ExceptionHandler(HttpMessageNotReadableException.class)
|
||||
public ResponseEntity<ApiResponse<Void>> handleUnreadable(HttpMessageNotReadableException ex, HttpServletRequest request) {
|
||||
log.warn("[{}] {} request body unreadable: {}", request.getMethod(), request.getRequestURI(), ex.getMessage());
|
||||
return ResponseEntity.badRequest().body(ApiResponse.badRequest("请求体格式错误"));
|
||||
}
|
||||
|
||||
@ExceptionHandler(HttpRequestMethodNotSupportedException.class)
|
||||
public ResponseEntity<ApiResponse<Void>> handleMethodNotSupported(HttpRequestMethodNotSupportedException ex, HttpServletRequest request) {
|
||||
log.warn("[{}] {} method not supported: {}", request.getMethod(), request.getRequestURI(), ex.getMethod());
|
||||
return ResponseEntity.status(HttpStatus.METHOD_NOT_ALLOWED)
|
||||
.body(ApiResponse.error(405, "请求方法不支持: " + ex.getMethod()));
|
||||
}
|
||||
|
||||
@ExceptionHandler(AuthorizationDeniedException.class)
|
||||
public ResponseEntity<ApiResponse<Void>> handleAuthorizationDenied(AuthorizationDeniedException ex, HttpServletRequest request) {
|
||||
Authentication auth = SecurityContextHolder.getContext().getAuthentication();
|
||||
String principal = auth == null ? "anonymous" : String.valueOf(auth.getPrincipal());
|
||||
String authorities = auth == null ? "[]" : auth.getAuthorities().toString();
|
||||
log.warn("[{}] {} authorization denied: principal={} authorities={} reason={}",
|
||||
request.getMethod(), request.getRequestURI(), principal, authorities, ex.getMessage());
|
||||
return ResponseEntity.status(HttpStatus.FORBIDDEN)
|
||||
.body(ApiResponse.error(403, "权限不足"));
|
||||
}
|
||||
|
||||
@ExceptionHandler(Exception.class)
|
||||
@ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
|
||||
public ApiResponse<Void> handleGeneric(Exception ex) {
|
||||
log.error("Unhandled exception", ex);
|
||||
return ApiResponse.error(500, "Internal server error");
|
||||
public ResponseEntity<ApiResponse<Void>> handleGeneric(Exception ex, HttpServletRequest request) {
|
||||
log.error("[{}] {} unhandled exception: {}", request.getMethod(), request.getRequestURI(), ex.getMessage(), ex);
|
||||
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR)
|
||||
.body(ApiResponse.error(500, "服务器内部错误"));
|
||||
}
|
||||
|
||||
private HttpStatus resolveStatus(int code) {
|
||||
return switch (code) {
|
||||
case 400 -> HttpStatus.BAD_REQUEST;
|
||||
case 401 -> HttpStatus.UNAUTHORIZED;
|
||||
case 403 -> HttpStatus.FORBIDDEN;
|
||||
case 404 -> HttpStatus.NOT_FOUND;
|
||||
default -> HttpStatus.INTERNAL_SERVER_ERROR;
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@ -2,6 +2,7 @@ package com.xuqm.im.controller;
|
||||
|
||||
import com.xuqm.common.exception.BusinessException;
|
||||
import com.xuqm.common.model.ApiResponse;
|
||||
import jakarta.servlet.http.HttpServletRequest;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.http.HttpStatus;
|
||||
@ -10,6 +11,8 @@ import org.springframework.http.converter.HttpMessageNotReadableException;
|
||||
import org.springframework.security.authorization.AuthorizationDeniedException;
|
||||
import org.springframework.security.core.Authentication;
|
||||
import org.springframework.security.core.context.SecurityContextHolder;
|
||||
import org.springframework.web.HttpRequestMethodNotSupportedException;
|
||||
import org.springframework.web.bind.MissingServletRequestParameterException;
|
||||
import org.springframework.web.bind.MethodArgumentNotValidException;
|
||||
import org.springframework.web.bind.annotation.ExceptionHandler;
|
||||
import org.springframework.web.bind.annotation.RestControllerAdvice;
|
||||
@ -20,47 +23,68 @@ public class GlobalExceptionHandler {
|
||||
private static final Logger log = LoggerFactory.getLogger(GlobalExceptionHandler.class);
|
||||
|
||||
@ExceptionHandler(BusinessException.class)
|
||||
public ResponseEntity<ApiResponse<Void>> handleBusiness(BusinessException e) {
|
||||
return ResponseEntity.status(resolveStatus(e.getCode()))
|
||||
.body(ApiResponse.error(e.getCode(), e.getMessage()));
|
||||
public ResponseEntity<ApiResponse<Void>> handleBusiness(BusinessException ex, HttpServletRequest request) {
|
||||
if (ex.getCode() >= 500) {
|
||||
log.error("[{}] {} code={} msg={}", request.getMethod(), request.getRequestURI(), ex.getCode(), ex.getMessage(), ex);
|
||||
} else {
|
||||
log.warn("[{}] {} code={} msg={}", request.getMethod(), request.getRequestURI(), ex.getCode(), ex.getMessage());
|
||||
}
|
||||
return ResponseEntity.status(resolveStatus(ex.getCode()))
|
||||
.body(ApiResponse.error(ex.getCode(), ex.getMessage()));
|
||||
}
|
||||
|
||||
@ExceptionHandler(MethodArgumentNotValidException.class)
|
||||
public ResponseEntity<ApiResponse<Void>> handleValidation(MethodArgumentNotValidException e) {
|
||||
return ResponseEntity.badRequest().body(ApiResponse.badRequest(e.getBindingResult()
|
||||
.getFieldErrors()
|
||||
.stream()
|
||||
public ResponseEntity<ApiResponse<Void>> handleValidation(MethodArgumentNotValidException ex, HttpServletRequest request) {
|
||||
String message = ex.getBindingResult().getFieldErrors().stream()
|
||||
.findFirst()
|
||||
.map(error -> error.getDefaultMessage())
|
||||
.orElse("参数错误")));
|
||||
.orElse("参数错误");
|
||||
log.warn("[{}] {} validation failed: {}", request.getMethod(), request.getRequestURI(), message);
|
||||
return ResponseEntity.badRequest().body(ApiResponse.badRequest(message));
|
||||
}
|
||||
|
||||
@ExceptionHandler(MissingServletRequestParameterException.class)
|
||||
public ResponseEntity<ApiResponse<Void>> handleMissingParam(MissingServletRequestParameterException ex, HttpServletRequest request) {
|
||||
log.warn("[{}] {} missing param: {}", request.getMethod(), request.getRequestURI(), ex.getParameterName());
|
||||
return ResponseEntity.badRequest()
|
||||
.body(ApiResponse.badRequest("缺少必填参数: " + ex.getParameterName()));
|
||||
}
|
||||
|
||||
@ExceptionHandler(IllegalArgumentException.class)
|
||||
public ResponseEntity<ApiResponse<Void>> handleIllegalArgument(IllegalArgumentException e) {
|
||||
return ResponseEntity.badRequest().body(ApiResponse.badRequest(e.getMessage() == null ? "参数错误" : e.getMessage()));
|
||||
public ResponseEntity<ApiResponse<Void>> handleIllegalArgument(IllegalArgumentException ex, HttpServletRequest request) {
|
||||
log.warn("[{}] {} illegal argument: {}", request.getMethod(), request.getRequestURI(), ex.getMessage());
|
||||
return ResponseEntity.badRequest().body(ApiResponse.badRequest(ex.getMessage() == null ? "参数错误" : ex.getMessage()));
|
||||
}
|
||||
|
||||
@ExceptionHandler(HttpMessageNotReadableException.class)
|
||||
public ResponseEntity<ApiResponse<Void>> handleUnreadable(HttpMessageNotReadableException e) {
|
||||
public ResponseEntity<ApiResponse<Void>> handleUnreadable(HttpMessageNotReadableException ex, HttpServletRequest request) {
|
||||
log.warn("[{}] {} request body unreadable: {}", request.getMethod(), request.getRequestURI(), ex.getMessage());
|
||||
return ResponseEntity.badRequest().body(ApiResponse.badRequest("请求体格式错误"));
|
||||
}
|
||||
|
||||
@ExceptionHandler(HttpRequestMethodNotSupportedException.class)
|
||||
public ResponseEntity<ApiResponse<Void>> handleMethodNotSupported(HttpRequestMethodNotSupportedException ex, HttpServletRequest request) {
|
||||
log.warn("[{}] {} method not supported: {}", request.getMethod(), request.getRequestURI(), ex.getMethod());
|
||||
return ResponseEntity.status(HttpStatus.METHOD_NOT_ALLOWED)
|
||||
.body(ApiResponse.error(405, "请求方法不支持: " + ex.getMethod()));
|
||||
}
|
||||
|
||||
@ExceptionHandler(AuthorizationDeniedException.class)
|
||||
public ResponseEntity<ApiResponse<Void>> handleAuthorizationDenied(AuthorizationDeniedException e) {
|
||||
public ResponseEntity<ApiResponse<Void>> handleAuthorizationDenied(AuthorizationDeniedException ex, HttpServletRequest request) {
|
||||
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
|
||||
String principal = authentication == null ? null : String.valueOf(authentication.getPrincipal());
|
||||
String principal = authentication == null ? "anonymous" : String.valueOf(authentication.getPrincipal());
|
||||
String authorities = authentication == null ? "[]" : authentication.getAuthorities().toString();
|
||||
log.warn("Access denied path={} principal={} authorities={} reason={}",
|
||||
"im-service", principal, authorities, e.getMessage());
|
||||
log.warn("[{}] {} authorization denied: principal={} authorities={} reason={}",
|
||||
request.getMethod(), request.getRequestURI(), principal, authorities, ex.getMessage());
|
||||
return ResponseEntity.status(HttpStatus.FORBIDDEN)
|
||||
.body(ApiResponse.error(403, "Forbidden: current token lacks required role"));
|
||||
.body(ApiResponse.error(403, "权限不足"));
|
||||
}
|
||||
|
||||
@ExceptionHandler(Exception.class)
|
||||
public ResponseEntity<ApiResponse<Void>> handleException(Exception e) {
|
||||
log.error("Unhandled exception", e);
|
||||
public ResponseEntity<ApiResponse<Void>> handleException(Exception ex, HttpServletRequest request) {
|
||||
log.error("[{}] {} unhandled exception: {}", request.getMethod(), request.getRequestURI(), ex.getMessage(), ex);
|
||||
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR)
|
||||
.body(ApiResponse.error(500, "服务异常"));
|
||||
.body(ApiResponse.error(500, "服务器内部错误"));
|
||||
}
|
||||
|
||||
private HttpStatus resolveStatus(int code) {
|
||||
|
||||
@ -2,7 +2,17 @@ package com.xuqm.license.controller;
|
||||
|
||||
import com.xuqm.common.exception.BusinessException;
|
||||
import com.xuqm.common.model.ApiResponse;
|
||||
import jakarta.servlet.http.HttpServletRequest;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.http.converter.HttpMessageNotReadableException;
|
||||
import org.springframework.security.authorization.AuthorizationDeniedException;
|
||||
import org.springframework.security.core.Authentication;
|
||||
import org.springframework.security.core.context.SecurityContextHolder;
|
||||
import org.springframework.web.HttpRequestMethodNotSupportedException;
|
||||
import org.springframework.web.bind.MissingServletRequestParameterException;
|
||||
import org.springframework.web.bind.MethodArgumentNotValidException;
|
||||
import org.springframework.web.bind.annotation.ExceptionHandler;
|
||||
import org.springframework.web.bind.annotation.RestControllerAdvice;
|
||||
@ -10,17 +20,80 @@ import org.springframework.web.bind.annotation.RestControllerAdvice;
|
||||
@RestControllerAdvice
|
||||
public class GlobalExceptionHandler {
|
||||
|
||||
private static final Logger log = LoggerFactory.getLogger(GlobalExceptionHandler.class);
|
||||
|
||||
@ExceptionHandler(BusinessException.class)
|
||||
public ResponseEntity<ApiResponse<Void>> handleBusinessException(BusinessException e) {
|
||||
return ResponseEntity.status(e.getCode()).body(ApiResponse.error(e.getCode(), e.getMessage()));
|
||||
public ResponseEntity<ApiResponse<Void>> handleBusiness(BusinessException ex, HttpServletRequest request) {
|
||||
if (ex.getCode() >= 500) {
|
||||
log.error("[{}] {} code={} msg={}", request.getMethod(), request.getRequestURI(), ex.getCode(), ex.getMessage(), ex);
|
||||
} else {
|
||||
log.warn("[{}] {} code={} msg={}", request.getMethod(), request.getRequestURI(), ex.getCode(), ex.getMessage());
|
||||
}
|
||||
return ResponseEntity.status(resolveStatus(ex.getCode()))
|
||||
.body(ApiResponse.error(ex.getCode(), ex.getMessage()));
|
||||
}
|
||||
|
||||
@ExceptionHandler(MethodArgumentNotValidException.class)
|
||||
public ResponseEntity<ApiResponse<Void>> handleValidationException(MethodArgumentNotValidException e) {
|
||||
String detail = e.getBindingResult().getFieldErrors().stream()
|
||||
public ResponseEntity<ApiResponse<Void>> handleValidation(MethodArgumentNotValidException ex, HttpServletRequest request) {
|
||||
String detail = ex.getBindingResult().getFieldErrors().stream()
|
||||
.map(f -> f.getField() + ": " + f.getDefaultMessage())
|
||||
.reduce((a, b) -> a + "; " + b)
|
||||
.orElse("Invalid request");
|
||||
.orElse("参数错误");
|
||||
log.warn("[{}] {} validation failed: {}", request.getMethod(), request.getRequestURI(), detail);
|
||||
return ResponseEntity.badRequest().body(ApiResponse.badRequest(detail));
|
||||
}
|
||||
|
||||
@ExceptionHandler(MissingServletRequestParameterException.class)
|
||||
public ResponseEntity<ApiResponse<Void>> handleMissingParam(MissingServletRequestParameterException ex, HttpServletRequest request) {
|
||||
log.warn("[{}] {} missing param: {}", request.getMethod(), request.getRequestURI(), ex.getParameterName());
|
||||
return ResponseEntity.badRequest()
|
||||
.body(ApiResponse.badRequest("缺少必填参数: " + ex.getParameterName()));
|
||||
}
|
||||
|
||||
@ExceptionHandler(IllegalArgumentException.class)
|
||||
public ResponseEntity<ApiResponse<Void>> handleIllegalArgument(IllegalArgumentException ex, HttpServletRequest request) {
|
||||
log.warn("[{}] {} illegal argument: {}", request.getMethod(), request.getRequestURI(), ex.getMessage());
|
||||
return ResponseEntity.badRequest().body(ApiResponse.badRequest(ex.getMessage() == null ? "参数错误" : ex.getMessage()));
|
||||
}
|
||||
|
||||
@ExceptionHandler(HttpMessageNotReadableException.class)
|
||||
public ResponseEntity<ApiResponse<Void>> handleUnreadable(HttpMessageNotReadableException ex, HttpServletRequest request) {
|
||||
log.warn("[{}] {} request body unreadable: {}", request.getMethod(), request.getRequestURI(), ex.getMessage());
|
||||
return ResponseEntity.badRequest().body(ApiResponse.badRequest("请求体格式错误"));
|
||||
}
|
||||
|
||||
@ExceptionHandler(HttpRequestMethodNotSupportedException.class)
|
||||
public ResponseEntity<ApiResponse<Void>> handleMethodNotSupported(HttpRequestMethodNotSupportedException ex, HttpServletRequest request) {
|
||||
log.warn("[{}] {} method not supported: {}", request.getMethod(), request.getRequestURI(), ex.getMethod());
|
||||
return ResponseEntity.status(HttpStatus.METHOD_NOT_ALLOWED)
|
||||
.body(ApiResponse.error(405, "请求方法不支持: " + ex.getMethod()));
|
||||
}
|
||||
|
||||
@ExceptionHandler(AuthorizationDeniedException.class)
|
||||
public ResponseEntity<ApiResponse<Void>> handleAuthorizationDenied(AuthorizationDeniedException ex, HttpServletRequest request) {
|
||||
Authentication auth = SecurityContextHolder.getContext().getAuthentication();
|
||||
String principal = auth == null ? "anonymous" : String.valueOf(auth.getPrincipal());
|
||||
String authorities = auth == null ? "[]" : auth.getAuthorities().toString();
|
||||
log.warn("[{}] {} authorization denied: principal={} authorities={} reason={}",
|
||||
request.getMethod(), request.getRequestURI(), principal, authorities, ex.getMessage());
|
||||
return ResponseEntity.status(HttpStatus.FORBIDDEN)
|
||||
.body(ApiResponse.error(403, "权限不足"));
|
||||
}
|
||||
|
||||
@ExceptionHandler(Exception.class)
|
||||
public ResponseEntity<ApiResponse<Void>> handleException(Exception ex, HttpServletRequest request) {
|
||||
log.error("[{}] {} unhandled exception: {}", request.getMethod(), request.getRequestURI(), ex.getMessage(), ex);
|
||||
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR)
|
||||
.body(ApiResponse.error(500, "服务器内部错误"));
|
||||
}
|
||||
|
||||
private HttpStatus resolveStatus(int code) {
|
||||
return switch (code) {
|
||||
case 400 -> HttpStatus.BAD_REQUEST;
|
||||
case 401 -> HttpStatus.UNAUTHORIZED;
|
||||
case 403 -> HttpStatus.FORBIDDEN;
|
||||
case 404 -> HttpStatus.NOT_FOUND;
|
||||
default -> HttpStatus.INTERNAL_SERVER_ERROR;
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@ -2,9 +2,18 @@ package com.xuqm.push.controller;
|
||||
|
||||
import com.xuqm.common.exception.BusinessException;
|
||||
import com.xuqm.common.model.ApiResponse;
|
||||
import jakarta.servlet.http.HttpServletRequest;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.http.converter.HttpMessageNotReadableException;
|
||||
import org.springframework.security.authorization.AuthorizationDeniedException;
|
||||
import org.springframework.security.core.Authentication;
|
||||
import org.springframework.security.core.context.SecurityContextHolder;
|
||||
import org.springframework.web.HttpRequestMethodNotSupportedException;
|
||||
import org.springframework.web.bind.MissingServletRequestParameterException;
|
||||
import org.springframework.web.bind.MethodArgumentNotValidException;
|
||||
import org.springframework.web.bind.annotation.ExceptionHandler;
|
||||
import org.springframework.web.bind.annotation.RestControllerAdvice;
|
||||
|
||||
@ -14,15 +23,77 @@ public class GlobalExceptionHandler {
|
||||
private static final Logger log = LoggerFactory.getLogger(GlobalExceptionHandler.class);
|
||||
|
||||
@ExceptionHandler(BusinessException.class)
|
||||
public ResponseEntity<ApiResponse<Void>> handleBusiness(BusinessException ex) {
|
||||
return ResponseEntity.status(ex.getCode())
|
||||
public ResponseEntity<ApiResponse<Void>> handleBusiness(BusinessException ex, HttpServletRequest request) {
|
||||
if (ex.getCode() >= 500) {
|
||||
log.error("[{}] {} code={} msg={}", request.getMethod(), request.getRequestURI(), ex.getCode(), ex.getMessage(), ex);
|
||||
} else {
|
||||
log.warn("[{}] {} code={} msg={}", request.getMethod(), request.getRequestURI(), ex.getCode(), ex.getMessage());
|
||||
}
|
||||
return ResponseEntity.status(resolveStatus(ex.getCode()))
|
||||
.body(ApiResponse.error(ex.getCode(), ex.getMessage()));
|
||||
}
|
||||
|
||||
@ExceptionHandler(MethodArgumentNotValidException.class)
|
||||
public ResponseEntity<ApiResponse<Void>> handleValidation(MethodArgumentNotValidException ex, HttpServletRequest request) {
|
||||
String message = ex.getBindingResult().getFieldErrors().stream()
|
||||
.findFirst()
|
||||
.map(error -> error.getDefaultMessage())
|
||||
.orElse("参数错误");
|
||||
log.warn("[{}] {} validation failed: {}", request.getMethod(), request.getRequestURI(), message);
|
||||
return ResponseEntity.badRequest().body(ApiResponse.badRequest(message));
|
||||
}
|
||||
|
||||
@ExceptionHandler(MissingServletRequestParameterException.class)
|
||||
public ResponseEntity<ApiResponse<Void>> handleMissingParam(MissingServletRequestParameterException ex, HttpServletRequest request) {
|
||||
log.warn("[{}] {} missing param: {}", request.getMethod(), request.getRequestURI(), ex.getParameterName());
|
||||
return ResponseEntity.badRequest()
|
||||
.body(ApiResponse.badRequest("缺少必填参数: " + ex.getParameterName()));
|
||||
}
|
||||
|
||||
@ExceptionHandler(IllegalArgumentException.class)
|
||||
public ResponseEntity<ApiResponse<Void>> handleIllegalArgument(IllegalArgumentException ex, HttpServletRequest request) {
|
||||
log.warn("[{}] {} illegal argument: {}", request.getMethod(), request.getRequestURI(), ex.getMessage());
|
||||
return ResponseEntity.badRequest().body(ApiResponse.badRequest(ex.getMessage() == null ? "参数错误" : ex.getMessage()));
|
||||
}
|
||||
|
||||
@ExceptionHandler(HttpMessageNotReadableException.class)
|
||||
public ResponseEntity<ApiResponse<Void>> handleUnreadable(HttpMessageNotReadableException ex, HttpServletRequest request) {
|
||||
log.warn("[{}] {} request body unreadable: {}", request.getMethod(), request.getRequestURI(), ex.getMessage());
|
||||
return ResponseEntity.badRequest().body(ApiResponse.badRequest("请求体格式错误"));
|
||||
}
|
||||
|
||||
@ExceptionHandler(HttpRequestMethodNotSupportedException.class)
|
||||
public ResponseEntity<ApiResponse<Void>> handleMethodNotSupported(HttpRequestMethodNotSupportedException ex, HttpServletRequest request) {
|
||||
log.warn("[{}] {} method not supported: {}", request.getMethod(), request.getRequestURI(), ex.getMethod());
|
||||
return ResponseEntity.status(HttpStatus.METHOD_NOT_ALLOWED)
|
||||
.body(ApiResponse.error(405, "请求方法不支持: " + ex.getMethod()));
|
||||
}
|
||||
|
||||
@ExceptionHandler(AuthorizationDeniedException.class)
|
||||
public ResponseEntity<ApiResponse<Void>> handleAuthorizationDenied(AuthorizationDeniedException ex, HttpServletRequest request) {
|
||||
Authentication auth = SecurityContextHolder.getContext().getAuthentication();
|
||||
String principal = auth == null ? "anonymous" : String.valueOf(auth.getPrincipal());
|
||||
String authorities = auth == null ? "[]" : auth.getAuthorities().toString();
|
||||
log.warn("[{}] {} authorization denied: principal={} authorities={} reason={}",
|
||||
request.getMethod(), request.getRequestURI(), principal, authorities, ex.getMessage());
|
||||
return ResponseEntity.status(HttpStatus.FORBIDDEN)
|
||||
.body(ApiResponse.error(403, "权限不足"));
|
||||
}
|
||||
|
||||
@ExceptionHandler(Exception.class)
|
||||
public ResponseEntity<ApiResponse<Void>> handleUnexpected(Exception ex) {
|
||||
log.error("Unexpected error", ex);
|
||||
return ResponseEntity.status(500)
|
||||
.body(ApiResponse.error(500, ex.getClass().getSimpleName() + ": " + ex.getMessage()));
|
||||
public ResponseEntity<ApiResponse<Void>> handleUnexpected(Exception ex, HttpServletRequest request) {
|
||||
log.error("[{}] {} unhandled exception: {}", request.getMethod(), request.getRequestURI(), ex.getMessage(), ex);
|
||||
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR)
|
||||
.body(ApiResponse.error(500, "服务器内部错误"));
|
||||
}
|
||||
|
||||
private HttpStatus resolveStatus(int code) {
|
||||
return switch (code) {
|
||||
case 400 -> HttpStatus.BAD_REQUEST;
|
||||
case 401 -> HttpStatus.UNAUTHORIZED;
|
||||
case 403 -> HttpStatus.FORBIDDEN;
|
||||
case 404 -> HttpStatus.NOT_FOUND;
|
||||
default -> HttpStatus.INTERNAL_SERVER_ERROR;
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@ -0,0 +1,192 @@
|
||||
package com.xuqm.tenant.controller;
|
||||
|
||||
import com.xuqm.tenant.config.PrivateDeploymentProperties;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.PathVariable;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RequestParam;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
import javax.sql.DataSource;
|
||||
import java.sql.Connection;
|
||||
import java.sql.DatabaseMetaData;
|
||||
import java.sql.PreparedStatement;
|
||||
import java.sql.ResultSet;
|
||||
import java.sql.ResultSetMetaData;
|
||||
import java.sql.SQLException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
@RestController
|
||||
@RequestMapping("/api/system/database")
|
||||
public class DatabaseController {
|
||||
|
||||
private final PrivateDeploymentProperties deployProps;
|
||||
private final DataSource dataSource;
|
||||
|
||||
public DatabaseController(PrivateDeploymentProperties deployProps, DataSource dataSource) {
|
||||
this.deployProps = deployProps;
|
||||
this.dataSource = dataSource;
|
||||
}
|
||||
|
||||
@GetMapping("/tables")
|
||||
public ResponseEntity<?> listTables() {
|
||||
if (!deployProps.isPrivate()) {
|
||||
return ResponseEntity.status(403).body(Map.of("message", "此接口仅在私有化部署可用"));
|
||||
}
|
||||
try (Connection conn = dataSource.getConnection()) {
|
||||
DatabaseMetaData meta = conn.getMetaData();
|
||||
String catalog = conn.getCatalog();
|
||||
ResultSet rs = meta.getTables(catalog, null, "%", new String[]{"TABLE"});
|
||||
List<Map<String, Object>> tables = new ArrayList<>();
|
||||
while (rs.next()) {
|
||||
Map<String, Object> t = new LinkedHashMap<>();
|
||||
t.put("name", rs.getString("TABLE_NAME"));
|
||||
t.put("comment", rs.getString("REMARKS"));
|
||||
tables.add(t);
|
||||
}
|
||||
return ResponseEntity.ok(Map.of("data", tables));
|
||||
} catch (SQLException e) {
|
||||
return ResponseEntity.status(500).body(Map.of("message", "查询表列表失败: " + e.getMessage()));
|
||||
}
|
||||
}
|
||||
|
||||
@GetMapping("/tables/{tableName}/columns")
|
||||
public ResponseEntity<?> listColumns(@PathVariable String tableName) {
|
||||
if (!deployProps.isPrivate()) {
|
||||
return ResponseEntity.status(403).body(Map.of("message", "此接口仅在私有化部署可用"));
|
||||
}
|
||||
if (!isAllowedTable(tableName)) {
|
||||
return ResponseEntity.status(403).body(Map.of("message", "不允许访问该表"));
|
||||
}
|
||||
try (Connection conn = dataSource.getConnection()) {
|
||||
DatabaseMetaData meta = conn.getMetaData();
|
||||
String catalog = conn.getCatalog();
|
||||
ResultSet rs = meta.getColumns(catalog, null, tableName, "%");
|
||||
List<Map<String, Object>> columns = new ArrayList<>();
|
||||
while (rs.next()) {
|
||||
Map<String, Object> c = new LinkedHashMap<>();
|
||||
c.put("name", rs.getString("COLUMN_NAME"));
|
||||
c.put("type", rs.getString("TYPE_NAME"));
|
||||
c.put("size", rs.getInt("COLUMN_SIZE"));
|
||||
c.put("nullable", rs.getInt("NULLABLE") == DatabaseMetaData.columnNullable);
|
||||
c.put("comment", rs.getString("REMARKS"));
|
||||
columns.add(c);
|
||||
}
|
||||
return ResponseEntity.ok(Map.of("data", columns));
|
||||
} catch (SQLException e) {
|
||||
return ResponseEntity.status(500).body(Map.of("message", "查询列信息失败: " + e.getMessage()));
|
||||
}
|
||||
}
|
||||
|
||||
@GetMapping("/tables/{tableName}/data")
|
||||
public ResponseEntity<?> queryData(
|
||||
@PathVariable String tableName,
|
||||
@RequestParam(defaultValue = "0") int page,
|
||||
@RequestParam(defaultValue = "50") int size,
|
||||
@RequestParam(required = false) String keyword,
|
||||
@RequestParam(required = false) String sortColumn,
|
||||
@RequestParam(required = false, defaultValue = "ASC") String sortDirection) {
|
||||
if (!deployProps.isPrivate()) {
|
||||
return ResponseEntity.status(403).body(Map.of("message", "此接口仅在私有化部署可用"));
|
||||
}
|
||||
if (!isAllowedTable(tableName)) {
|
||||
return ResponseEntity.status(403).body(Map.of("message", "不允许访问该表"));
|
||||
}
|
||||
if (size > 200) size = 200;
|
||||
|
||||
try (Connection conn = dataSource.getConnection()) {
|
||||
// Get columns info for keyword search
|
||||
List<String> textColumns = new ArrayList<>();
|
||||
DatabaseMetaData meta = conn.getMetaData();
|
||||
String catalog = conn.getCatalog();
|
||||
ResultSet colRs = meta.getColumns(catalog, null, tableName, "%");
|
||||
List<String> allColumns = new ArrayList<>();
|
||||
while (colRs.next()) {
|
||||
String colName = colRs.getString("COLUMN_NAME");
|
||||
String typeName = colRs.getString("TYPE_NAME");
|
||||
allColumns.add(colName);
|
||||
if (typeName != null && (typeName.contains("CHAR") || typeName.contains("TEXT") || typeName.contains("VARCHAR"))) {
|
||||
textColumns.add(colName);
|
||||
}
|
||||
}
|
||||
|
||||
// Build WHERE clause for keyword search
|
||||
StringBuilder whereClause = new StringBuilder();
|
||||
List<Object> params = new ArrayList<>();
|
||||
if (keyword != null && !keyword.isBlank() && !textColumns.isEmpty()) {
|
||||
whereClause.append(" WHERE ");
|
||||
for (int i = 0; i < textColumns.size(); i++) {
|
||||
if (i > 0) whereClause.append(" OR ");
|
||||
String safeCol = sanitizeIdentifier(textColumns.get(i));
|
||||
whereClause.append(safeCol).append(" LIKE ?");
|
||||
params.add("%" + keyword + "%");
|
||||
}
|
||||
}
|
||||
|
||||
// Count total
|
||||
String countSql = "SELECT COUNT(*) FROM " + sanitizeIdentifier(tableName) + whereClause;
|
||||
long total = 0;
|
||||
try (PreparedStatement ps = conn.prepareStatement(countSql)) {
|
||||
for (int i = 0; i < params.size(); i++) ps.setObject(i + 1, params.get(i));
|
||||
try (ResultSet rs = ps.executeQuery()) {
|
||||
if (rs.next()) total = rs.getLong(1);
|
||||
}
|
||||
}
|
||||
|
||||
// Build ORDER BY
|
||||
String orderClause = "";
|
||||
if (sortColumn != null && !sortColumn.isBlank() && allColumns.contains(sortColumn)) {
|
||||
String dir = "DESC".equalsIgnoreCase(sortDirection) ? "DESC" : "ASC";
|
||||
orderClause = " ORDER BY " + sanitizeIdentifier(sortColumn) + " " + dir;
|
||||
}
|
||||
|
||||
// Query data with pagination
|
||||
String dataSql = "SELECT * FROM " + sanitizeIdentifier(tableName) + whereClause + orderClause
|
||||
+ " LIMIT ? OFFSET ?";
|
||||
List<Map<String, Object>> rows = new ArrayList<>();
|
||||
try (PreparedStatement ps = conn.prepareStatement(dataSql)) {
|
||||
int idx = 1;
|
||||
for (Object p : params) ps.setObject(idx++, p);
|
||||
ps.setInt(idx++, size);
|
||||
ps.setInt(idx, page * size);
|
||||
try (ResultSet rs = ps.executeQuery()) {
|
||||
ResultSetMetaData rsmd = rs.getMetaData();
|
||||
int colCount = rsmd.getColumnCount();
|
||||
while (rs.next()) {
|
||||
Map<String, Object> row = new LinkedHashMap<>();
|
||||
for (int i = 1; i <= colCount; i++) {
|
||||
row.put(rsmd.getColumnName(i), rs.getObject(i));
|
||||
}
|
||||
rows.add(row);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int totalPages = size > 0 ? (int) Math.ceil((double) total / size) : 0;
|
||||
Map<String, Object> result = new LinkedHashMap<>();
|
||||
result.put("columns", allColumns);
|
||||
result.put("rows", rows);
|
||||
result.put("total", total);
|
||||
result.put("totalPages", totalPages);
|
||||
result.put("page", page);
|
||||
result.put("size", size);
|
||||
return ResponseEntity.ok(Map.of("data", result));
|
||||
} catch (SQLException e) {
|
||||
return ResponseEntity.status(500).body(Map.of("message", "查询数据失败: " + e.getMessage()));
|
||||
}
|
||||
}
|
||||
|
||||
private boolean isAllowedTable(String tableName) {
|
||||
// Allow any table in the current database - the table name comes from our own listTables() API
|
||||
return tableName != null && tableName.matches("[a-zA-Z0-9_]+");
|
||||
}
|
||||
|
||||
private static String sanitizeIdentifier(String identifier) {
|
||||
// Backtick-quote to prevent SQL injection
|
||||
return "`" + identifier.replace("`", "``") + "`";
|
||||
}
|
||||
}
|
||||
@ -2,12 +2,18 @@ package com.xuqm.tenant.controller;
|
||||
|
||||
import com.xuqm.common.exception.BusinessException;
|
||||
import com.xuqm.common.model.ApiResponse;
|
||||
import jakarta.servlet.http.HttpServletRequest;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.http.converter.HttpMessageNotReadableException;
|
||||
import org.springframework.security.authorization.AuthorizationDeniedException;
|
||||
import org.springframework.security.core.Authentication;
|
||||
import org.springframework.security.core.context.SecurityContextHolder;
|
||||
import org.springframework.validation.FieldError;
|
||||
import org.springframework.web.HttpRequestMethodNotSupportedException;
|
||||
import org.springframework.web.bind.MissingServletRequestParameterException;
|
||||
import org.springframework.web.bind.MethodArgumentNotValidException;
|
||||
import org.springframework.web.bind.annotation.ExceptionHandler;
|
||||
import org.springframework.web.bind.annotation.RestControllerAdvice;
|
||||
@ -20,33 +26,66 @@ public class GlobalExceptionHandler {
|
||||
private static final Logger log = LoggerFactory.getLogger(GlobalExceptionHandler.class);
|
||||
|
||||
@ExceptionHandler(BusinessException.class)
|
||||
public ResponseEntity<ApiResponse<Void>> handle(BusinessException ex) {
|
||||
public ResponseEntity<ApiResponse<Void>> handle(BusinessException ex, HttpServletRequest request) {
|
||||
if (ex.getCode() >= 500) {
|
||||
log.error("[{}] {} code={} msg={}", request.getMethod(), request.getRequestURI(), ex.getCode(), ex.getMessage(), ex);
|
||||
} else {
|
||||
log.warn("[{}] {} code={} msg={}", request.getMethod(), request.getRequestURI(), ex.getCode(), ex.getMessage());
|
||||
}
|
||||
return ResponseEntity.status(resolveStatus(ex.getCode()))
|
||||
.body(ApiResponse.error(ex.getCode(), ex.getMessage()));
|
||||
}
|
||||
|
||||
@ExceptionHandler(MethodArgumentNotValidException.class)
|
||||
public ResponseEntity<ApiResponse<Void>> handle(MethodArgumentNotValidException ex) {
|
||||
public ResponseEntity<ApiResponse<Void>> handle(MethodArgumentNotValidException ex, HttpServletRequest request) {
|
||||
String message = ex.getBindingResult().getFieldErrors().stream()
|
||||
.map(FieldError::getDefaultMessage)
|
||||
.collect(Collectors.joining("; "));
|
||||
log.warn("[{}] {} validation failed: {}", request.getMethod(), request.getRequestURI(), message);
|
||||
return ResponseEntity.badRequest().body(ApiResponse.badRequest(message));
|
||||
}
|
||||
|
||||
@ExceptionHandler(MissingServletRequestParameterException.class)
|
||||
public ResponseEntity<ApiResponse<Void>> handle(MissingServletRequestParameterException ex, HttpServletRequest request) {
|
||||
log.warn("[{}] {} missing param: {}", request.getMethod(), request.getRequestURI(), ex.getParameterName());
|
||||
return ResponseEntity.badRequest()
|
||||
.body(ApiResponse.badRequest("缺少必填参数: " + ex.getParameterName()));
|
||||
}
|
||||
|
||||
@ExceptionHandler(IllegalArgumentException.class)
|
||||
public ResponseEntity<ApiResponse<Void>> handle(IllegalArgumentException ex) {
|
||||
public ResponseEntity<ApiResponse<Void>> handle(IllegalArgumentException ex, HttpServletRequest request) {
|
||||
log.warn("[{}] {} illegal argument: {}", request.getMethod(), request.getRequestURI(), ex.getMessage());
|
||||
return ResponseEntity.badRequest()
|
||||
.body(ApiResponse.badRequest(ex.getMessage() == null ? "参数错误" : ex.getMessage()));
|
||||
}
|
||||
|
||||
@ExceptionHandler(HttpMessageNotReadableException.class)
|
||||
public ResponseEntity<ApiResponse<Void>> handle(HttpMessageNotReadableException ex) {
|
||||
public ResponseEntity<ApiResponse<Void>> handle(HttpMessageNotReadableException ex, HttpServletRequest request) {
|
||||
log.warn("[{}] {} request body unreadable: {}", request.getMethod(), request.getRequestURI(), ex.getMessage());
|
||||
return ResponseEntity.badRequest().body(ApiResponse.badRequest("请求体格式错误"));
|
||||
}
|
||||
|
||||
@ExceptionHandler(HttpRequestMethodNotSupportedException.class)
|
||||
public ResponseEntity<ApiResponse<Void>> handle(HttpRequestMethodNotSupportedException ex, HttpServletRequest request) {
|
||||
log.warn("[{}] {} method not supported: {}", request.getMethod(), request.getRequestURI(), ex.getMethod());
|
||||
return ResponseEntity.status(HttpStatus.METHOD_NOT_ALLOWED)
|
||||
.body(ApiResponse.error(405, "请求方法不支持: " + ex.getMethod()));
|
||||
}
|
||||
|
||||
@ExceptionHandler(AuthorizationDeniedException.class)
|
||||
public ResponseEntity<ApiResponse<Void>> handle(AuthorizationDeniedException ex, HttpServletRequest request) {
|
||||
Authentication auth = SecurityContextHolder.getContext().getAuthentication();
|
||||
String principal = auth == null ? "anonymous" : String.valueOf(auth.getPrincipal());
|
||||
String authorities = auth == null ? "[]" : auth.getAuthorities().toString();
|
||||
log.warn("[{}] {} authorization denied: principal={} authorities={} reason={}",
|
||||
request.getMethod(), request.getRequestURI(), principal, authorities, ex.getMessage());
|
||||
return ResponseEntity.status(HttpStatus.FORBIDDEN)
|
||||
.body(ApiResponse.error(403, "权限不足"));
|
||||
}
|
||||
|
||||
@ExceptionHandler(Exception.class)
|
||||
public ResponseEntity<ApiResponse<Void>> handle(Exception ex) {
|
||||
log.error("Unhandled exception", ex);
|
||||
public ResponseEntity<ApiResponse<Void>> handle(Exception ex, HttpServletRequest request) {
|
||||
log.error("[{}] {} unhandled exception: {}", request.getMethod(), request.getRequestURI(), ex.getMessage(), ex);
|
||||
return ResponseEntity.internalServerError()
|
||||
.body(ApiResponse.error(500, "服务器内部错误"));
|
||||
}
|
||||
|
||||
@ -2,11 +2,17 @@ package com.xuqm.update.controller;
|
||||
|
||||
import com.xuqm.common.exception.BusinessException;
|
||||
import com.xuqm.common.model.ApiResponse;
|
||||
import jakarta.servlet.http.HttpServletRequest;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.http.converter.HttpMessageNotReadableException;
|
||||
import org.springframework.security.authorization.AuthorizationDeniedException;
|
||||
import org.springframework.security.core.Authentication;
|
||||
import org.springframework.security.core.context.SecurityContextHolder;
|
||||
import org.springframework.web.HttpRequestMethodNotSupportedException;
|
||||
import org.springframework.web.bind.MissingServletRequestParameterException;
|
||||
import org.springframework.web.bind.MethodArgumentNotValidException;
|
||||
import org.springframework.web.bind.annotation.ExceptionHandler;
|
||||
import org.springframework.web.bind.annotation.RestControllerAdvice;
|
||||
@ -19,33 +25,66 @@ public class GlobalExceptionHandler {
|
||||
private static final Logger log = LoggerFactory.getLogger(GlobalExceptionHandler.class);
|
||||
|
||||
@ExceptionHandler(BusinessException.class)
|
||||
public ResponseEntity<ApiResponse<Void>> handle(BusinessException ex) {
|
||||
public ResponseEntity<ApiResponse<Void>> handle(BusinessException ex, HttpServletRequest request) {
|
||||
if (ex.getCode() >= 500) {
|
||||
log.error("[{}] {} code={} msg={}", request.getMethod(), request.getRequestURI(), ex.getCode(), ex.getMessage(), ex);
|
||||
} else {
|
||||
log.warn("[{}] {} code={} msg={}", request.getMethod(), request.getRequestURI(), ex.getCode(), ex.getMessage());
|
||||
}
|
||||
return ResponseEntity.status(resolveStatus(ex.getCode()))
|
||||
.body(ApiResponse.error(ex.getCode(), ex.getMessage()));
|
||||
}
|
||||
|
||||
@ExceptionHandler(MethodArgumentNotValidException.class)
|
||||
public ResponseEntity<ApiResponse<Void>> handle(MethodArgumentNotValidException ex) {
|
||||
public ResponseEntity<ApiResponse<Void>> handle(MethodArgumentNotValidException ex, HttpServletRequest request) {
|
||||
String message = ex.getBindingResult().getFieldErrors().stream()
|
||||
.map(f -> f.getDefaultMessage())
|
||||
.collect(Collectors.joining("; "));
|
||||
log.warn("[{}] {} validation failed: {}", request.getMethod(), request.getRequestURI(), message);
|
||||
return ResponseEntity.badRequest().body(ApiResponse.badRequest(message));
|
||||
}
|
||||
|
||||
@ExceptionHandler(MissingServletRequestParameterException.class)
|
||||
public ResponseEntity<ApiResponse<Void>> handle(MissingServletRequestParameterException ex, HttpServletRequest request) {
|
||||
log.warn("[{}] {} missing param: {}", request.getMethod(), request.getRequestURI(), ex.getParameterName());
|
||||
return ResponseEntity.badRequest()
|
||||
.body(ApiResponse.badRequest("缺少必填参数: " + ex.getParameterName()));
|
||||
}
|
||||
|
||||
@ExceptionHandler(IllegalArgumentException.class)
|
||||
public ResponseEntity<ApiResponse<Void>> handle(IllegalArgumentException ex) {
|
||||
public ResponseEntity<ApiResponse<Void>> handle(IllegalArgumentException ex, HttpServletRequest request) {
|
||||
log.warn("[{}] {} illegal argument: {}", request.getMethod(), request.getRequestURI(), ex.getMessage());
|
||||
return ResponseEntity.badRequest()
|
||||
.body(ApiResponse.badRequest(ex.getMessage() == null ? "参数错误" : ex.getMessage()));
|
||||
}
|
||||
|
||||
@ExceptionHandler(HttpMessageNotReadableException.class)
|
||||
public ResponseEntity<ApiResponse<Void>> handle(HttpMessageNotReadableException ex) {
|
||||
public ResponseEntity<ApiResponse<Void>> handle(HttpMessageNotReadableException ex, HttpServletRequest request) {
|
||||
log.warn("[{}] {} request body unreadable: {}", request.getMethod(), request.getRequestURI(), ex.getMessage());
|
||||
return ResponseEntity.badRequest().body(ApiResponse.badRequest("请求体格式错误"));
|
||||
}
|
||||
|
||||
@ExceptionHandler(HttpRequestMethodNotSupportedException.class)
|
||||
public ResponseEntity<ApiResponse<Void>> handle(HttpRequestMethodNotSupportedException ex, HttpServletRequest request) {
|
||||
log.warn("[{}] {} method not supported: {}", request.getMethod(), request.getRequestURI(), ex.getMethod());
|
||||
return ResponseEntity.status(HttpStatus.METHOD_NOT_ALLOWED)
|
||||
.body(ApiResponse.error(405, "请求方法不支持: " + ex.getMethod()));
|
||||
}
|
||||
|
||||
@ExceptionHandler(AuthorizationDeniedException.class)
|
||||
public ResponseEntity<ApiResponse<Void>> handle(AuthorizationDeniedException ex, HttpServletRequest request) {
|
||||
Authentication auth = SecurityContextHolder.getContext().getAuthentication();
|
||||
String principal = auth == null ? "anonymous" : String.valueOf(auth.getPrincipal());
|
||||
String authorities = auth == null ? "[]" : auth.getAuthorities().toString();
|
||||
log.warn("[{}] {} authorization denied: principal={} authorities={} reason={}",
|
||||
request.getMethod(), request.getRequestURI(), principal, authorities, ex.getMessage());
|
||||
return ResponseEntity.status(HttpStatus.FORBIDDEN)
|
||||
.body(ApiResponse.error(403, "权限不足"));
|
||||
}
|
||||
|
||||
@ExceptionHandler(Exception.class)
|
||||
public ResponseEntity<ApiResponse<Void>> handle(Exception ex) {
|
||||
log.error("Unhandled exception", ex);
|
||||
public ResponseEntity<ApiResponse<Void>> handle(Exception ex, HttpServletRequest request) {
|
||||
log.error("[{}] {} unhandled exception: {}", request.getMethod(), request.getRequestURI(), ex.getMessage(), ex);
|
||||
return ResponseEntity.internalServerError()
|
||||
.body(ApiResponse.error(500, "服务器内部错误"));
|
||||
}
|
||||
|
||||
正在加载...
在新工单中引用
屏蔽一个用户