把我兩次書寫全局異常捕獲統(tǒng)一處理返回JSON的經(jīng)驗(yàn)分享給大家迁客。其中真的還是有點(diǎn)小進(jìn)步的郭宝。里面涉及到泛型和建造者的使用,還是能學(xué)到挺多的掷漱。
實(shí)現(xiàn)的思路是
- 全局異常的抓取粘室。
- 封裝統(tǒng)一返回結(jié)果對(duì)象
第一種方式:我記得好像是參考阿里巴巴的黃勇同志寫出來的。其實(shí)很簡(jiǎn)單通過ControllerAdvice做一Controller的AOP卜范。然后通過攔截對(duì)應(yīng)的Exception可以自定義返回對(duì)應(yīng)的HttpStatus育特。然后封裝Reponse統(tǒng)一返回?cái)?shù)據(jù)即可。甚至連正常返回都可以封裝為一個(gè)Exception返回先朦,可以參照下面的StatusSuccess,具體實(shí)現(xiàn)比較簡(jiǎn)單犬缨。
別人親測(cè)喳魏,這里有一個(gè)天大的坑,入門的小伙伴要注意了怀薛。事務(wù)回滾的異常處理是需要extend RunTimeException 而繼承Exception 是沒辦法做到事務(wù)回滾的刺彩。
第二種方式:是我參考簡(jiǎn)書小伙伴的文章寫出來的。另外加入了建造者設(shè)計(jì)模式枝恋,個(gè)人見解使用簡(jiǎn)單创倔,代碼可讀性更好。實(shí)現(xiàn)思路更第一種類似焚碌,但是在代碼上有了比較大的改動(dòng)畦攘。尤其是泛型的使用,和設(shè)計(jì)模式的使用十电。
兩種方式的比較:
弱點(diǎn)一:Controller返回的都是Object 知押。雖然可以滿足大部分的業(yè)務(wù),但是過了兩個(gè)月鹃骂,前端問你這個(gè)接口返回的是什么東西台盯。多半你要找到你的dao層才能判斷了。而第二種方式則可以避免這種問題畏线。使用了泛型静盅。很容易讓你清楚的知道data里面放到是什么
弱點(diǎn)二:代碼可讀性更強(qiáng)了。不會(huì)在Service里面throw new StatusSuccess()了寝殴。這段代碼始終讓我感覺奇奇怪怪的又說不是是啥蒿叠。
第二種實(shí)現(xiàn)方式,則是在Controller里面使用Builder設(shè)計(jì)模式杯矩,讓代碼更加優(yōu)雅帥氣栈虚,MVC分層明顯,代碼可讀性強(qiáng)史隆,真的是一個(gè)很棒的實(shí)現(xiàn)方式魂务。
第二種實(shí)現(xiàn)方式,直接上代碼了。
1 在controller 的實(shí)現(xiàn)效果粘姜。代碼簡(jiǎn)潔鬓照。可讀性??孤紧。
@RestController
@RequestMapping("/app/user")
public class ApiController {
@RequestMapping("data")
public RestResult<Object> text() throws Exception {
throw new Exception();
}
@RequestMapping("getData")
public RestResult<String> getData() throws Exception {
return RestResultGenrator.build(
xxxService.getData();
);
}
}
2 統(tǒng)一異常抓取豺裆。注意看我的RestResultBuild的構(gòu)建『畔裕看起來更加美觀臭猜。
/**
* RestExceptionHandler
*
* @author zf
* @date 9/23/16
*/
@ControllerAdvice(annotations = RestController.class)
public class RestExceptionHandler {
private static final Logger LOGGER = LoggerFactory.getLogger(RestExceptionHandler.class);
@ExceptionHandler
@ResponseBody
@ResponseStatus(HttpStatus.OK)
private <T> RestResult<T> runtimeExceptionHandler(Exception e){
LOGGER.error("------->error !" ,e);
return new RestResultBuilder<T>()
.setErrorCode(ErrorCode.ERROR)
.setMessage(ErrorCode.ErrorMessage.ERROR)
.build();
}
@ExceptionHandler(MethodArgumentNotValidException.class)
@ResponseBody
@ResponseStatus(HttpStatus.OK)
private <T> RestResult<T> illegalParamsExceptionHandler(MethodArgumentNotValidException e) {
LOGGER.error("---------> invalid request!", e);
return new RestResultBuilder<T>()
.setErrorCode(ErrorCode.ERROR_METHOD)
.setMessage(ErrorCode.ErrorMessage.ERROR_METHOD)
.build();
}
@ExceptionHandler(ExpireException.class)
@ResponseBody
@ResponseStatus(HttpStatus.OK)
private <T> RestResult<T> expireHandler(MethodArgumentNotValidException e) {
LOGGER.error("---------> invalid expire!", e);
return new RestResultBuilder<T>()
.setErrorCode(ErrorCode.EXPIRED)
.setMessage(ErrorCode.ErrorMessage.EXPIRED)
.build();
}
}
3 使用統(tǒng)一泛型處理,增強(qiáng)Controller的可讀性押蚤。
/**
* RestResult
*
* @author zf
* @date 9/23/16
*/
public class RestResult <T>{
private int errorCode;
private String message;
private T data;
private RestResult(){}
protected RestResult(int errorCode, String message, T data) {
this.errorCode = errorCode;
this.message = message;
this.data = data;
}
public int getErrorCode() {
return errorCode;
}
public RestResult setErrorCode(int errorCode) {
this.errorCode = errorCode;
return this;
}
public String getMessage() {
return message;
}
public RestResult<T> setMessage(String message) {
this.message = message;
return this;
}
public T getData() {
return data;
}
public RestResult<T> setData(T data) {
this.data = data;
return this;
}
}
4 使用builder設(shè)計(jì)模式構(gòu)建統(tǒng)一返回對(duì)象蔑歌。
/**
* RestResultBuilder
*
* @author zf
* @date 9/23/16
*/
public class RestResultBuilder<T> {
private int errorCode = ErrorCode.VALID;
private String message =ErrorCode.ErrorMessage.VALID;;
private T data ;
protected RestResultBuilder<T> setErrorCode(int errorCode){
this.errorCode = errorCode;
return this;
}
public RestResultBuilder<T> setMessage(String message){
this.message = message;
return this;
}
public RestResultBuilder<T> setData(T data){
this.data = data;
return this;
}
public RestResult<T> build(){
return new RestResult<T>(errorCode,message,data);
}
}
5 我是為了不想寫new三個(gè)字母,所以寫了第五個(gè)類揽碘,Genrator次屠。
public class RestResultGenrator {
public static <T> RestResult<T> build(){
return build(null);
}
public static <T> RestResult<T> build(T t){
return new RestResultBuilder<T>().setData(t).build();
}
}
下面是第一種實(shí)現(xiàn)方式。
- 全局異常抓取類
@ControllerAdvice
@ResponseBody
public class ExceptionAdvice {
Logger logger = Logger.getGlobal();
/**
* 400 - Bad Request
*/
@ResponseStatus(HttpStatus.BAD_REQUEST)
@ExceptionHandler(HttpMessageNotReadableException.class)
public Response handleHttpMessageNotReadableException(
HttpMessageNotReadableException e) {
logger.info("參數(shù)解析失敗" + e);
return new Response().failure("could_not_read_json");
}
/**
* 405 - Method Not Allowed
*/
@ResponseStatus(HttpStatus.METHOD_NOT_ALLOWED)
@ExceptionHandler(HttpRequestMethodNotSupportedException.class)
public Response handleHttpRequestMethodNotSupportedException(
HttpRequestMethodNotSupportedException e) {
logger.info("不支持當(dāng)前請(qǐng)求方法" + e);
return new Response().failure("request_method_not_supported");
}
/**
* 415 - Unsupported Media Type
*/
@ResponseStatus(HttpStatus.UNSUPPORTED_MEDIA_TYPE)
@ExceptionHandler(HttpMediaTypeNotSupportedException.class)
public Response handleHttpMediaTypeNotSupportedException(Exception e) {
logger.info("不支持當(dāng)前媒體類型" + e);
return new Response().failure("content_type_not_supported");
}
/**
* 500 - Internal Server Error
*/
@ResponseStatus(HttpStatus.OK)
@ExceptionHandler(Exception.class)
public Response handleException(Exception e) {
e.printStackTrace();
return new Response().failure(e.getMessage());
}
/**
* - Internal Server Error
*/
@ResponseStatus(HttpStatus.OK)
@ExceptionHandler(StatusSuccess.class)
public Response handleSuccessStatus(StatusSuccess e) {
logger.info("StatusSuccess");
return new Response(Response.SUCCESS_CODE, e.getMessage(), e.getData());
}
/**
* 500 - Internal Server Error
*/
@ResponseStatus(HttpStatus.OK)
@ExceptionHandler(ExpireException.class)
public Response handleExipreStatus(Exception e) {
logger.info("內(nèi)部錯(cuò)誤");
return new Response().timeOut(e.getMessage());
}
/**
* 500 - 內(nèi)部錯(cuò)誤
*/
@ResponseStatus(HttpStatus.OK)
@ExceptionHandler(IllegalArgumentException.class)
public Response parameterException(IllegalArgumentException e) {
logger.info("非法參數(shù)" + e.toString());
return new Response().failure(e.getMessage());
}
/**
* 500 - Internal Server Error
*/
@ResponseStatus(HttpStatus.OK)
@ExceptionHandler(ShopStatusException.class)
public Response errorShopStatus(Exception e) {
logger.info("店鋪狀態(tài)異常");
return new Response().shopStatusException(e.getMessage());
}
/**
* 訂單狀態(tài)異常攔截器
*
* @author yangyanchao
* @date 2016年8月16日
* @param e
* @return
*/
public Response orderStatusException(Exception e) {
return new Response().shopStatusException(e.getMessage());
}
}
- 統(tǒng)一返回實(shí)體類
/**
* Response
*
* @author zf
* @date 16/3/21
*/
public class Response {
private static final String OK = "success";
private static final String ERROR = "failure";
private static final String TIMEOUT = "expired";
public static final int SUCCESS_CODE = 1;
public static final int FAIL_CODE = 0;
public static final int TOKEN_INVALID = -1;
public static final int SHOP_ABNORMAL = -2;
@JsonView(BaseView.BaseResponse.class)
private int status;
@JsonView(BaseView.BaseResponse.class)
private String msg;
@JsonView(BaseView.BaseResponse.class)
private Object data;
public Response success() {
this.status = SUCCESS_CODE;
this.msg = OK;
return this;
}
public Response success(Object data) {
this.status = SUCCESS_CODE;
this.msg = OK;
this.data = data;
return this;
}
public Response failure() {
this.status = FAIL_CODE;
this.msg = ERROR;
return this;
}
public Response() {
}
public Response(int status, String msg, Object data) {
this.status = status;
this.msg = msg;
this.data = data;
}
public Response expireException(String msg) {
this.status = TOKEN_INVALID;
this.msg = msg;
return this;
}
public Response timeOut(String msg) {
this.status = TOKEN_INVALID;
this.msg = msg;
return this;
}
public Response shopStatusException(String msg) {
this.status = SHOP_ABNORMAL;
this.msg = msg;
return this;
}
public int getStatus() {
return status;
}
public void setStatus(int status) {
this.status = status;
}
public String getMsg() {
return msg;
}
public void setMsg(String msg) {
this.msg = msg;
}
public void setData(Object data) {
this.data = data;
}
public Response failure(String message) {
this.status = FAIL_CODE;
this.msg = message;
return this;
}
public Object getData() {
return data;
}
}
3.service 使用例子
/**
* 保存用戶
*
* @param principle
* 用戶信息
* @param uadId
* 用戶地址id
* @return 保存
*/
public void save(Principle principle, Integer uadId) throws NotSamePeopleException, StatusSuccess {
AsUserAddress userAddress = asUserAddressMapper.selectByPrimaryKey(uadId);
if (userAddress.getUserId().equals(principle.getUserId())) {
principle.setAddressId(uadId);
AsUser u = new AsUser();
u.setUserId(principle.getUserId());
u.setAddressId(uadId);
asUsersMapper.updateByPrimaryKeySelective(u);
throw new StatusSuccess();
} else {
throw new NotSamePeopleException();
}
}
3 . 正確返回的異常處理雳刺,注意看這里是封裝了data 的劫灶。
/**
* OrderStatusException
*
* @author zf
* @date 16/7/14
*/
public class StatusSuccess extends Exception{
private static String msg = "操作成功";
private Object data;
public StatusSuccess(){
super(msg);
}
public StatusSuccess(String msg) {
super(msg);
}
public StatusSuccess(Object data) {
super(msg);
this.data = data;
}
public StatusSuccess(String message, Object data) {
super(message);
this.data = data;
}
public static String getMsg() {
return msg;
}
public static void setMsg(String msg) {
StatusSuccess.msg = msg;
}
public Object getData() {
return data;
}
public void setData(Object data) {
this.data = data;
}
}
最后用postMan得出最終相同的結(jié)果。