From a08fac24a6297ad0a5c344018771321c10ae089a Mon Sep 17 00:00:00 2001 From: johnniang <1340692778@qq.com> Date: Wed, 20 Feb 2019 23:44:25 +0800 Subject: [PATCH] Complete ControllerExceptionHandler --- .../cc/ryanc/halo/model/dto/JsonResult.java | 15 +++ .../cc/ryanc/halo/utils/ExceptionUtils.java | 33 +++++ .../base/ControllerExceptionHandler.java | 113 ++++++++++++++++++ .../halo/web/interceptor/ApiInterceptor.java | 12 +- 4 files changed, 170 insertions(+), 3 deletions(-) create mode 100644 src/main/java/cc/ryanc/halo/utils/ExceptionUtils.java create mode 100644 src/main/java/cc/ryanc/halo/web/controller/base/ControllerExceptionHandler.java diff --git a/src/main/java/cc/ryanc/halo/model/dto/JsonResult.java b/src/main/java/cc/ryanc/halo/model/dto/JsonResult.java index 3218ff1d4..294582724 100644 --- a/src/main/java/cc/ryanc/halo/model/dto/JsonResult.java +++ b/src/main/java/cc/ryanc/halo/model/dto/JsonResult.java @@ -23,11 +23,19 @@ public class JsonResult { */ private String msg; + /** + * Dev message.(only setting in dev environment) + */ + private String devMsg; + /** * 返回的数据 */ private Object result; + public JsonResult() { + } + /** * 只返回状态码 * @@ -71,4 +79,11 @@ public class JsonResult { this.code = code; this.result = result; } + + public JsonResult(Integer code, String msg, String devMsg, Object result) { + this.code = code; + this.msg = msg; + this.devMsg = devMsg; + this.result = result; + } } diff --git a/src/main/java/cc/ryanc/halo/utils/ExceptionUtils.java b/src/main/java/cc/ryanc/halo/utils/ExceptionUtils.java new file mode 100644 index 000000000..a4bdb0216 --- /dev/null +++ b/src/main/java/cc/ryanc/halo/utils/ExceptionUtils.java @@ -0,0 +1,33 @@ +package cc.ryanc.halo.utils; + +import java.io.PrintWriter; +import java.io.StringWriter; + +/** + * Exception utilities. + *

Part from apache commons lang3 project.

+ * + * @author johnniang + * @see "org.apache.commons.lang3.exception.ExceptionUtils" + */ +public class ExceptionUtils { + + /** + *

Gets the stack trace from a Throwable as a String.

+ * + *

The result of this method vary by JDK version as this method + * uses {@link Throwable#printStackTrace(java.io.PrintWriter)}. + * On JDK1.3 and earlier, the cause exception will not be shown + * unless the specified throwable alters printStackTrace.

+ * + * @param throwable the Throwable to be examined + * @return the stack trace as generated by the exception's + * printStackTrace(PrintWriter) method + */ + public static String getStackTrace(final Throwable throwable) { + final StringWriter sw = new StringWriter(); + final PrintWriter pw = new PrintWriter(sw, true); + throwable.printStackTrace(pw); + return sw.getBuffer().toString(); + } +} diff --git a/src/main/java/cc/ryanc/halo/web/controller/base/ControllerExceptionHandler.java b/src/main/java/cc/ryanc/halo/web/controller/base/ControllerExceptionHandler.java new file mode 100644 index 000000000..f0fa0725c --- /dev/null +++ b/src/main/java/cc/ryanc/halo/web/controller/base/ControllerExceptionHandler.java @@ -0,0 +1,113 @@ +package cc.ryanc.halo.web.controller.base; + +import cc.ryanc.halo.exception.HaloException; +import cc.ryanc.halo.logging.Logger; +import cc.ryanc.halo.model.dto.JsonResult; +import cc.ryanc.halo.utils.ExceptionUtils; +import cc.ryanc.halo.utils.ValidationUtils; +import org.springframework.dao.DataIntegrityViolationException; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.util.Assert; +import org.springframework.web.bind.MethodArgumentNotValidException; +import org.springframework.web.bind.MissingServletRequestParameterException; +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.servlet.NoHandlerFoundException; + +import javax.validation.ConstraintViolationException; +import java.util.Map; + +/** + * Exception handler of controller. + */ +@RestControllerAdvice +public class ControllerExceptionHandler { + + private final Logger log = Logger.getLogger(getClass()); + + @ExceptionHandler(DataIntegrityViolationException.class) + @ResponseStatus(HttpStatus.BAD_REQUEST) + public JsonResult handleDataIntegrityViolationException(DataIntegrityViolationException e) { + JsonResult jsonResult = handleBaseException(e); + if (e.getCause() instanceof org.hibernate.exception.ConstraintViolationException) { + jsonResult = handleBaseException(e.getCause()); + } + jsonResult.setMsg("Failed to validate request parameter"); + return jsonResult; + } + + @ExceptionHandler(MissingServletRequestParameterException.class) + @ResponseStatus(HttpStatus.BAD_REQUEST) + public JsonResult handleMissingServletRequestParameterException(MissingServletRequestParameterException e) { + JsonResult jsonResult = handleBaseException(e); + jsonResult.setMsg(String.format("Missing request parameter, required %s type %s parameter", e.getParameterType(), e.getParameterName())); + return jsonResult; + } + + @ExceptionHandler(ConstraintViolationException.class) + @ResponseStatus(HttpStatus.BAD_REQUEST) + public JsonResult handleConstraintViolationException(ConstraintViolationException e) { + JsonResult jsonResult = handleBaseException(e); + jsonResult.setCode(HttpStatus.BAD_REQUEST.value()); + jsonResult.setMsg("Filed validation error"); + jsonResult.setResult(e.getConstraintViolations()); + return jsonResult; + } + + @ExceptionHandler(MethodArgumentNotValidException.class) + @ResponseStatus(HttpStatus.BAD_REQUEST) + public JsonResult handleMethodArgumentNotValidException(MethodArgumentNotValidException e) { + JsonResult jsonResult = handleBaseException(e); + jsonResult.setCode(HttpStatus.BAD_REQUEST.value()); + jsonResult.setMsg("Filed validation error"); + Map errMap = ValidationUtils.mapWithFieldError(e.getBindingResult().getFieldErrors()); + jsonResult.setResult(errMap); + return jsonResult; + } + + @ExceptionHandler(NoHandlerFoundException.class) + @ResponseStatus(HttpStatus.BAD_GATEWAY) + public JsonResult handleNoHandlerFoundException(NoHandlerFoundException e) { + JsonResult jsonResult = handleBaseException(e); + HttpStatus status = HttpStatus.BAD_GATEWAY; + jsonResult.setCode(status.value()); + jsonResult.setMsg(status.getReasonPhrase()); + return jsonResult; + } + + @ExceptionHandler(HaloException.class) + public ResponseEntity handleHaloException(HaloException e) { + JsonResult jsonResult = handleBaseException(e); + jsonResult.setCode(e.getStatus().value()); + jsonResult.setResult(e.getErrorData()); + return new ResponseEntity<>(jsonResult, e.getStatus()); + } + + @ExceptionHandler(Exception.class) + @ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR) + public JsonResult handleGlobalException(Exception e) { + JsonResult jsonResult = handleBaseException(e); + HttpStatus status = HttpStatus.INTERNAL_SERVER_ERROR; + jsonResult.setCode(status.value()); + jsonResult.setMsg(status.getReasonPhrase()); + return jsonResult; + } + + private JsonResult handleBaseException(Throwable t) { + Assert.notNull(t, "Throwable must not be null"); + + log.error("Captured an exception", t); + + JsonResult jsonResult = new JsonResult(); + jsonResult.setMsg(t.getMessage()); + + if (log.isDebugEnabled()) { + jsonResult.setDevMsg(ExceptionUtils.getStackTrace(t)); + } + + return jsonResult; + } + +} diff --git a/src/main/java/cc/ryanc/halo/web/interceptor/ApiInterceptor.java b/src/main/java/cc/ryanc/halo/web/interceptor/ApiInterceptor.java index 06c7b4b4d..819b8a9e0 100644 --- a/src/main/java/cc/ryanc/halo/web/interceptor/ApiInterceptor.java +++ b/src/main/java/cc/ryanc/halo/web/interceptor/ApiInterceptor.java @@ -4,6 +4,7 @@ import cc.ryanc.halo.model.enums.BlogPropertiesEnum; import cc.ryanc.halo.model.enums.TrueFalseEnum; import cn.hutool.core.util.StrUtil; import com.fasterxml.jackson.databind.ObjectMapper; +import org.springframework.http.HttpStatus; import org.springframework.stereotype.Component; import org.springframework.web.servlet.HandlerInterceptor; import org.springframework.web.servlet.ModelAndView; @@ -28,6 +29,12 @@ public class ApiInterceptor implements HandlerInterceptor { private static final String TOKEN = "token"; + private final ObjectMapper objectMapper; + + public ApiInterceptor(ObjectMapper objectMapper) { + this.objectMapper = objectMapper; + } + @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { if (StrUtil.equals(TrueFalseEnum.TRUE.getDesc(), OPTIONS.get(BlogPropertiesEnum.API_STATUS.getProp()))) { @@ -37,10 +44,9 @@ public class ApiInterceptor implements HandlerInterceptor { response.setCharacterEncoding("UTF-8"); response.setContentType("application/json;charset=utf-8"); Map map = new HashMap<>(2); - ObjectMapper mapper = new ObjectMapper(); - map.put("code", 400); + map.put("code", HttpStatus.BAD_REQUEST.value()); map.put("msg", "Invalid Token"); - response.getWriter().write(mapper.writeValueAsString(map)); + response.getWriter().write(objectMapper.writeValueAsString(map)); return false; } }