目前是用SpringMVC時,往往使用ExceptionHandler去做Controller層的統(tǒng)一異常處理。
使用ExceptionHandler注解的異常處理方法可以使用很靈活的方法簽名。
可使用的參數(shù)類型
- 一個異常參數(shù)。聲明一個一般性的異?;蛘吒泳唧w的異常
- Request 和/或 response 對象(Servlet API 或 Portlet API)??梢赃x擇一個特定 - request/response的類型,比如ServletRequest / HttpServletRequest
- Session 對象
- WebRequest 或 NativeWebRequest
- Locale
- InputStream / Reader 訪問請求內(nèi)容
- OutputStream / Writer 生成響應(yīng)內(nèi)容
- Model
異常處理方法支持的返回值類型
- ModelAndView 對象 (Servlet MVC or Portlet MVC)
- Model 對象
- Map 對象,
- View 對象
- 被解析成一個視圖名稱的String 值
- @ResponseBody 注解的方法 (僅限Servlet) 設(shè)置響應(yīng)內(nèi)容
- HttpEntity<?> 或 ResponseEntity<?> (僅限Servlet) 設(shè)置響應(yīng)頭和響應(yīng)內(nèi)容
- void。方法自己處理了響應(yīng)。
如何在異常發(fā)生時輸出請求
發(fā)生異常時,不僅僅需要輸出異常本身,經(jīng)常還需要根據(jù)Request的具體內(nèi)容來分析、排查問題。
比如HttpRequestMethodNotSupportedException、HttpMessageConversionException等等,這些異常發(fā)生在業(yè)務(wù)代碼處理之前,業(yè)務(wù)代碼是無法獲取到request的數(shù)據(jù)的,發(fā)生異常時如果能夠看到請求body的具體內(nèi)容,那么處理起來就可以對癥下藥,事半功倍。
說起來簡單,做起來卻不是很順當,雖然ExcelptionHandler中可以傳入ServerletRequest作為入?yún)?,但是ServerletRequest的inputStream只能被讀取一次,發(fā)生異常的時候再想去讀取body只能悲催的得到一個已經(jīng)Closed的Stream。
找了一大圈,發(fā)現(xiàn)了一個有效的方法,感謝StackOverflow -_-~
使用ContentCachingRequestWrapper
- 通過過濾器將ServerletRequest封裝成ContentCachingRequestWrapper,body被讀取后,會被它緩存。
@Component
public class RequestWrapperFilter extends OncePerRequestFilter {
@Override
protected void doFilterInternal(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, FilterChain filterChain) throws ServletException, IOException {
filterChain.doFilter(new ContentCachingRequestWrapper(httpServletRequest), httpServletResponse);
}
}
- ExceptionHandler傳入ServletRequest,此時的ServletRequest就是ContentCachingRequestWrapper,輸出即可
@ExceptionHandler({HttpRequestMethodNotSupportedException.class,
HttpMessageConversionException.class,
TypeMismatchException.class})
public ResponseEntity<Response> returnMediaTypeNotSupportError(Exception ex, ServletRequest request) {
if (request != null && request instanceof ContentCachingRequestWrapper) {
ContentCachingRequestWrapper wrapper = (ContentCachingRequestWrapper) request;
logger.warn("BAD_REQUEST_BODY:{}", StringUtils.toEncodedString(wrapper.getContentAsByteArray(), Charset.forName(wrapper.getCharacterEncoding())));
}
.....