spring mvc捕獲異常時,如何判斷應該返回json還是跳轉錯誤頁面
在異常捕獲handler BusinessExceptionHandlerAdvice中
判斷控制器的方法是否有ResponseBody註解,如果有,則返回json,
關鍵問題是:
如何判斷控制器的方法是否有ResponseBody註解
我們先看看@ExceptionHandler方法中注入的參數有哪些?
1. ServletResponse.class 2. OutputStream.class 3. Writer.class 4. WebRequest.class 5. ServletRequest.class 6. MultipartRequest.class 7. HttpSession.class 8. Principal.class 9. Locale.class 10. InputStream.class 11. Reader.class
直接獲取控制器方法(java.lang.reflect.Method)的計劃破滅了,
那麼還有沒有其他方法呢?
有!通過異常的堆棧 StackTraceElement
那麼StackTraceElement有哪些信息呢?
所以我們可以通過反射獲取控制器方法,然後判斷是否有註解ResponseBody.class
具體實現(直接上代碼):
package oa.web.controller.handler;
import com.common.bean.BaseResponseDto;
import com.common.bean.exception.LogicBusinessException;
import com.common.util.ReflectHWUtils;
import com.common.util.SystemHWUtil;
import com.common.util.WebServletUtil;
import com.string.widget.util.ValueWidget;
import org.apache.log4j.Logger;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import java.io.IOException;
import java.io.PrintWriter;
import java.io.UnsupportedEncodingException;
import java.lang.reflect.Method;
import java.net.URLEncoder;
/**
* Created by whuanghkl on 3/30/16.
* 注意使用註解@ControllerAdvice作用域是全局Controller範圍
* 可應用到所有@RequestMapping類或方法上的@ExceptionHandler、@InitBinder、@ModelAttribute,在這裏是@ExceptionHandler<br />
* 用於檢測第三方接口,比如bsvc或cia的504,502等異常<br />
* 這些異常均屬於非業務異常,與業務毫無關係,所以單獨處理<br />
* 注意:StoreBusinessException 不要捕獲,否則無法被BusinessExceptionHandlerAdvice 截獲<br />
* 注意:傳遞url中的參數如果可能包含中文一定要URL編碼,
*/
@ControllerAdvice
public class BusinessExceptionHandlerAdvice {
public static Logger logger = Logger.getLogger(BusinessExceptionHandlerAdvice.class);
/***
* 判斷接口的註解是否是ResponseBody,是,那麼返回json,而不是跳轉錯誤頁面
* @param stackTraceElement
* @return
*/
public static boolean isControllerAction(StackTraceElement stackTraceElement) {
String className = stackTraceElement.getClassName();
if (className.endsWith("Controller")) {
try {
Class controllerClass = Class.forName(className);
Method actionMethod = ReflectHWUtils.getMethod(controllerClass, stackTraceElement.getMethodName(), ResponseBody.class);
if (null == actionMethod) {
return false;
}
return true;
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
return false;
}
/***
* 判斷接口的註解是否是ResponseBody,是,那麼返回json,而不是跳轉錯誤頁面<br />
* 限制最多循環4次,否則影響性能
* @param stackTraceElements
* @return
*/
public static boolean isControllerAction(StackTraceElement[] stackTraceElements) {
int length = stackTraceElements.length;
if (length > 4) {
length = 4;
}
for (int i = 0; i < length; i++) {
StackTraceElement stackTraceElement = stackTraceElements[i];
if (isControllerAction(stackTraceElement)) {
return true;
}
}
return false;
}
@ExceptionHandler(LogicBusinessException.class)
// @RESPONSE_CONTENTTYPE_JSON_UTFStatus(HttpStatus.BAD_REQUEST)
// @ResponseBody
public String handleBusinessException(LogicBusinessException ex, HttpSession session, HttpServletRequest request, HttpServletResponse response) {
// return ClassUtils.getShortName(ex.getClass()) + ex.getMessage();
logger.error(ex);//{errorCode='1021', errorMessage='用戶不在組織的企業客戶身份中'}
logger.error("old url:" + request.getRequestURL());
logger.error("query string:" + request.getQueryString());
StackTraceElement[] stackTraceElements = ex.getStackTrace();
if (!ex.isWap()) {
boolean isControllerAction = isControllerAction(stackTraceElements);
System.out.println("isControllerAction :" + isControllerAction);
if (isControllerAction) {
ex.setWap(true);//表示返回json
}
}
if (ex.isWap() || WebServletUtil.getMobileOsInfo(request).isMobile()) {//如果是手機端
PrintWriter out = null;
try {
out = response.getWriter();
} catch (IOException e) {
e.printStackTrace();
}
if (ValueWidget.isNullOrEmpty(ex.getResponseBody())) {
out.print(new BaseResponseDto(ex.getErrorCode(), ex.getErrorMessage()).toJson());
} else {
out.print(ex.getResponseBody());
}
out.flush();
} else {
String redirectUrl = null;
if (ValueWidget.isNullOrEmpty(ex.getRedirectUrl())) {
String message = null;
message = getMessage(ex);
redirectUrl = "/error.html?error=" + ex.getErrorCode() + "&errorMessage=" + message;
} else {
redirectUrl = ex.getRedirectUrl();
}
logger.error("redirect url:" + redirectUrl);
try {
response.sendRedirect(redirectUrl);
} catch (IOException e) {
e.printStackTrace();
}
}
return null;
}
/***
*
* @param ex
* @param message :中文已經經過url 編碼
* @return
*/
private static String getMessage(LogicBusinessException ex) {
String message = null;
if (ValueWidget.isNullOrEmpty(ex.getErrorMessage())) {
message = SystemHWUtil.EMPTY;
} else {
try {
message = URLEncoder.encode(ex.getErrorMessage(), "UTF-8");
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
}
return message;
}
}
簡單說明下:
先判斷isWap是否爲true,如果爲true,說明已經手動指定返回json了,那麼就不用判斷了.
否則才需要通過StackTraceElement判斷