捕获spring RestController中的异常,并记录日志

在使用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

展开阅读全文

没有更多推荐了,返回首页