221 lines
8.5 KiB
Java
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();
|
|
}
|
|
}
|