Files
emo-grab/src/main/java/quant/rich/emoney/component/EmoneyAutoPlatformExceptionHandler.java
2025-11-03 10:33:30 +08:00

221 lines
8.5 KiB
Java

package quant.rich.emoney.component;
import lombok.extern.slf4j.Slf4j;
import quant.rich.emoney.exception.PageNotFoundException;
import quant.rich.emoney.exception.RException;
import quant.rich.emoney.pojo.dto.LayPageResp;
import quant.rich.emoney.pojo.dto.R;
import java.lang.reflect.Method;
import java.util.Collections;
import java.util.stream.Collectors;
import javax.security.auth.login.LoginException;
import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import jakarta.validation.ConstraintViolationException;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.validation.BindException;
import org.springframework.web.HttpRequestMethodNotSupportedException;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.ResponseStatus;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.HandlerMapping;
import org.springframework.web.servlet.resource.NoResourceFoundException;
import com.fasterxml.jackson.databind.JsonNode;
/**
* 异常处理
*/
@ControllerAdvice
@Slf4j
public class EmoneyAutoPlatformExceptionHandler {
@Autowired
HttpServletResponse response;
@Autowired
HttpServletRequest request;
@ExceptionHandler({
BindException.class,
ConstraintViolationException.class,
MethodArgumentNotValidException.class })
@ResponseBody
@ResponseStatus(HttpStatus.BAD_REQUEST)
public <Ex extends BindException> R<?> handleBindException(Ex ex) {
StringBuilder messageSb = new StringBuilder();
ex.getBindingResult().getAllErrors().forEach(error -> messageSb.append(error.getDefaultMessage()).append("\n"));
log.warn("Resolved Exception {}", messageSb.substring(0, messageSb.length() - 1));
log.warn(httpServletRequestToString(request));
return bodyOrPage(HttpStatus.BAD_REQUEST, messageSb.substring(0, messageSb.length() - 1));
}
@ExceptionHandler(LoginException.class)
@ResponseBody
@ResponseStatus(HttpStatus.NOT_ACCEPTABLE)
public R<?> handleLoginException(LoginException ex) {
return bodyOrPage(HttpStatus.NOT_ACCEPTABLE, ex);
}
@ExceptionHandler(RException.class)
@ResponseBody
public R<?> handleRException(RException ex) {
response.setStatus(ex.getHttpStatus().value());
if (ex.getLogRequest()) {
log.warn(httpServletRequestToString(request));
}
return bodyOrPage(ex.getHttpStatus(), ex);
}
@ExceptionHandler(ServletException.class)
@ResponseBody
public R<?> handleServletException(ServletException ex) {
HttpStatus httpStatus = HttpStatus.INTERNAL_SERVER_ERROR;
if (ex instanceof HttpRequestMethodNotSupportedException)
httpStatus = HttpStatus.METHOD_NOT_ALLOWED;
response.setStatus(httpStatus.value());
return bodyOrPage(httpStatus, ex);
}
@ExceptionHandler(value = Exception.class)
@ResponseBody
@ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
public R<?> handleException(Exception ex) {
if (ex instanceof PageNotFoundException) {
throw (PageNotFoundException) ex;
}
String message = null;
if (ex.getMessage() != null) {
message = ex.getMessage();
}
else if (ex.getCause() != null) {
message = ex.getCause().getMessage();
}
ex.printStackTrace();
log.warn("Resolved exception {}", message);
log.warn(httpServletRequestToString(request));
return bodyOrPage(HttpStatus.INTERNAL_SERVER_ERROR, ex);
}
/**
* 根据 @Autowired request 等自动判断当前请求是 text/html 还是 application/json
* @return
*/
private boolean isPage() {
boolean isPage = true;
String accept;
// 匹配当前 request 在控制层的方法,根据是否是
// @ResponseBody 注解、@RestController 注解、
// 已知的返回类型来判断返回的是否是页面
Object handler = request.getAttribute(HandlerMapping.BEST_MATCHING_HANDLER_ATTRIBUTE);
if (handler instanceof HandlerMethod handlerMethod) {
Method method = handlerMethod.getMethod();
Class<?> controllerClass = handlerMethod.getBeanType();
boolean hasResponseBody =
method.isAnnotationPresent(ResponseBody.class) ||
controllerClass.isAnnotationPresent(ResponseBody.class) ||
controllerClass.isAnnotationPresent(RestController.class);
Class<?> returnType = method.getReturnType();
boolean isDtoReturnType =
returnType.equals(R.class) ||
returnType.equals(LayPageResp.class) ||
JsonNode.class.isAssignableFrom(returnType);
if (hasResponseBody || isDtoReturnType) {
isPage = false;
}
}
if (isPage && (accept = request.getHeader("Accept")) != null) {
int indexOfHtml = accept.indexOf("text/html"), indexOfJson = accept.indexOf("application/json");
if (indexOfHtml == -1 && indexOfJson != -1) {
isPage = false;
} else if (indexOfHtml != -1 && indexOfJson != -1) {
isPage = indexOfHtml < indexOfJson;
}
}
return isPage;
}
private R<?> bodyOrPage(HttpStatus httpStatus, String message) {
boolean isPage = isPage();
if (isPage) {
throw message == null ? new RuntimeException("Page exception raised") : new RuntimeException(message);
}
R<?> r = message != null ?
R.status(httpStatus).setMessage(message).setData(message)
: R.status(httpStatus);
return r;
}
private R<?> bodyOrPage(HttpStatus httpStatus, Exception ex) {
boolean isPage = true;
String message = null;
if (ex instanceof RException ||
ex instanceof LoginException) {
isPage = false;
message = ex.getMessage();
}
else {
isPage = isPage();
}
if (isPage) {
if (ex instanceof NoResourceFoundException nrfe) {
if (StringUtils.isNotEmpty(nrfe.getMessage())
&& nrfe.getMessage().endsWith(" .well-known/appspecific/com.chrome.devtools.json.")) {
// 傻逼 Chrome 开发工具在本地调试时默认调用该地址
// see: https://blog.ni18.in/well-known-appspecific-com-chrome-devtools-json-request/
return null;
}
}
throw ex == null ? new RuntimeException("Page exception raised") : new RuntimeException(ex);
}
R<?> r = message != null ?
R.status(httpStatus).setMessage(message).setData(message)
: R.status(httpStatus);
return r;
}
private static String httpServletRequestToString(HttpServletRequest request) {
StringBuilder sb = new StringBuilder();
sb.append("Request Method = \"" + request.getMethod() + "\", ");
sb.append("Request URL Path = \"" + request.getRequestURL() + "\", ");
String headers = Collections.list(request.getHeaderNames()).stream()
.map(headerName -> headerName + " : " + Collections.list(request.getHeaders(headerName)))
.collect(Collectors.joining(", "));
if (headers.isEmpty()) {
sb.append("Request headers: NONE,");
} else {
sb.append("Request headers: [" + headers + "],");
}
String parameters = Collections.list(request.getParameterNames()).stream().map(p -> {
String[] values = request.getParameterValues(p);
return p + ": [" + StringUtils.join(values, ", ") + "]";
}).collect(Collectors.joining(", "));
if (parameters.isEmpty()) {
sb.append("Request parameters: NONE.");
} else {
sb.append("Request parameters: [" + parameters + "].");
}
return sb.toString();
}
}