在使用spring的时候,如果Controller中抛出异常,会被spring显示在客户端界面,而日志中一般没有记录。
客户端对异常的显示也是经spring处理后的信息,没有堆栈,这不方便找错和改错。
可以通过RestControllerAdvice注解定义一个异常处理类来解决这个问题。
代码如下注:也可以不继承ResponseEntityExceptionHandler类,此处继承只是省了一些通用异常的处理。
import java.util.HashMap;
import java.util.Map;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import org.springframework.web.context.request.WebRequest;
import org.springframework.web.servlet.NoHandlerFoundException;
import org.springframework.web.servlet.mvc.method.annotation.ResponseEntityExceptionHandler;
/**
*
* RestController执行过程中发生异常会被此处捕获处理
*
*/
@RestControllerAdvice
public class ControllerExceptionHandler extends ResponseEntityExceptionHandler {
private static final Log log = LogFactory.getLog(ControllerExceptionHandler.class);
/**
* 通过ExceptionHandler来设置待捕获的异常,Throwable可捕获任何异常,但优先级最低,因此
* HttpRequestMethodNotSupportedException HttpMediaTypeNotSupportedException
* HttpMediaTypeNotAcceptableException MissingPathVariableException
* MissingServletRequestParameterException ServletRequestBindingException
* ConversionNotSupportedException TypeMismatchException
* HttpMessageNotReadableException HttpMessageNotWritableException
* MethodArgumentNotValidException MissingServletRequestPartException
* BindException NoHandlerFoundException AsyncRequestTimeoutException
* 等已经在父类声明捕获的异常不会被此方法处理。
*/
@ExceptionHandler(Throwable.class)
@ResponseBody
ResponseEntity<Object> handleControllerException(Throwable ex, WebRequest request) {
Map<String,String> responseBody = new HashMap<>();
// 这里控制返回给客户端的信息
responseBody.put("message","internal server error. " + ex.getMessage());
Exception e;
if(ex instanceof Exception) {
e = (Exception) ex;
}else {
e = new Exception(ex);
}
return handleExceptionInternal(e, responseBody, new HttpHeaders(), HttpStatus.INTERNAL_SERVER_ERROR, request);
}
/**
* 需要覆盖这个方法,并且在此方法里记录日志;查看ResponseEntityExceptionHandler源码可知,
* 有些异常被父类捕获,不会进入此类的handleControllerException,因此如果在handleControllerException
* 记录异常日志,会导致部分异常无日志
*/
@Override
protected ResponseEntity<Object> handleExceptionInternal(Exception ex, Object body,
HttpHeaders headers, HttpStatus status, WebRequest request) {
if(log.isErrorEnabled()) {
log.error("内部错误", ex);
}
return super.handleExceptionInternal(ex, body, headers, status, request);
}
}
此方法还可改变发生异常时给客户端传递的信息;无法通过Bean Validation时默认传递的信息太丰富了,可能被有恶意的利用。
最后,做个小广告,免费课程:用Spring Boot编写RESTful API
http://study.163.com/course/introduction.htm?courseId=1005213034