先建立一個crud的項目。
controller調用service調用mapper
以下以簡單代碼代替
- controller
@GetMapping("/getUserById")
public String getUserById(String id){
String userById = userService.getUserById(id);
return userById;
}
- service
@Override
public String getUserById(String id) {
// 模拟業務
User user = userMapper.selectById(id);
return user.toString();
}
上面代碼雖然是把資料傳回給前台了,但是沒有處理異常以及沒有一個明确的辨別告訴前端我是成功還是失敗了,此時我們需要封裝一下統一的成功失敗标志來告訴前台如何處理傳回資料。
使用Lombok 簡單封裝了一個CommonResponse類
@Data
public class CommonResponse {
/**
* 傳回業務碼用來判斷成功失敗
* 200 成功
* 500 失敗
*/
private String code;
/** 描述 */
private String massage;
/** 描述 */
private Object date;
public CommonResponse(String code, String massage, Object date) {
this.code = code;
this.massage = massage;
this.date = date;
}
public static CommonResponse succeed(){
return getCommonResponse(CodeEnum.SUCCESS.getCode(), CodeEnum.SUCCESS.getMassage(), null);
}
public static CommonResponse succeed(Object date){
return getCommonResponse(CodeEnum.SUCCESS.getCode(), CodeEnum.SUCCESS.getMassage(), date);
}
public static CommonResponse succeed(String massage,Object date){
return getCommonResponse(CodeEnum.SUCCESS.getCode(), massage, date);
}
public static CommonResponse error(String massage){
return getCommonResponse(CodeEnum.ERROR.getCode(), massage, null);
}
public static CommonResponse error(String code,String massage){
return getCommonResponse(code, massage, null);
}
public static CommonResponse error(){
return getCommonResponse(CodeEnum.ERROR.getCode(), CodeEnum.ERROR.getMassage(), null);
}
public static CommonResponse getCommonResponse(String code, String massage, Object date){
return new CommonResponse(code,massage,date);
}
}
傳回的controller使用統一的CommonResponse
@GetMapping("/getUserById")
public CommonResponse getUserById(String id){
String userById = userService.getUserById(id);
return CommonResponse.succeed(userById);
}
傳回
{
"code": "200",
"massage": "成功",
"date": "User(id=1, username=嘉文00, password=1000000, age=5555)"
}
上述傳回基本符合預期,但是當程式出現未知異常怎麼辦了。
對service改造下
@Override
public String getUserByIdException(String id) {
User user = userMapper.selectById(id);
// 模拟業務異常
int i=5/0;
return user.toString();
}
controller 改造
@GetMapping("/getUserById")
public CommonResponse getUserById(String id){
try{
String userById = userService.getUserById(id);
return CommonResponse.succeed(userById);
}catch(Exception e){
e.printStackTrace();
log.error(e.getMessage());
return CommonResponse.error(e.getMessage());
}
}
上面是不是也可以,通過trycatch來判斷如何傳回。但是這樣代碼中就會出現大量的try catch,是不是很不好看,我們能不能添加一個統一的try呢,答案是可以的。
使用spring提供的統一的異常處理
spring 提供了三種異常捕獲方式,個人比較推薦這一種
@Slf4j
@ControllerAdvice
public class ExceptionHandle {
/**
* 處理未知異常
* @param e
* @return
*/
@ExceptionHandler(Exception.class)
@ResponseBody
public CommonResponse handleException(Exception e){
log.error("系統異常:{}",e.getMessage());
return CommonResponse.error(e.getMessage());
}
/**
* 處理主動抛出的自定義異常
* @param e
* @return
*/
@ExceptionHandler(BusinessException.class)
@ResponseBody
public CommonResponse handleBusinessException(BusinessException e){
log.error("自定義異常:{}",e.getErrMassage());
return CommonResponse.error(e.getErrCode(),e.getErrMassage());
}
}
踩過的坑:
- 這個地方在寫的時候遇到一個坑,因為捕獲異常後的傳回值是CommonResponse,是以要加上注解 @ResponseBody 便于 格式轉換。
- 在配置@ExceptionHandler的時候不能配置兩個相同的Exception。否則會不知道使用哪個而報錯。
這時controller 已經很清晰了,如下:隻用處理好業務調用,無需處理向上抛出的異常。
@GetMapping("/getUserByIdException")
public CommonResponse getUserByIdException(String id){
String userById = userService.getUserByIdException(id);
return CommonResponse.succeed(userById);
}
@GetMapping("/getUserByIdBusinessException")
public CommonResponse getUserByIdBusinessException(String id){
String userById = userService.getUserByIdBusinessException(id);
return CommonResponse.succeed(userById);
}
當然有時候我們會遇到自己的校驗不通過來終止程式。我們可以throw 一個Exception 或者我們需要定制傳回碼,自定義一個異常類也行。如下簡單示例,大家可以根據自己的業務需求去自定義。
- 自定義異常類BusinessException
@Data
public class BusinessException extends RuntimeException{
private static final long serialVersionUID = 918204099850898995L;
private String errCode;
private String errMassage;
public BusinessException(String errCode,String errMassage){
super(errMassage);
this.errCode = errCode;
this.errMassage = errMassage;
}
}
- service傳回自定義異常
@Override
public String getUserByIdBusinessException(String id) {
User user = userMapper.selectById(id);
// 模拟業務異常
if("1".equals(id)){
throw new BusinessException("400","id為1的資料不支援查詢。");
}
return user.toString();
}
此時我們前端得到的傳回
## 請求
http://localhost:8088/getUserByIdBusinessException?id=1
## 傳回
{
"code": "400",
"massage": "id為1的資料不支援查詢。",
"date": null
}
以上就是統一異常捕獲跟統一的傳回。
另外我們實際項目為了使業務異常好看,統一,我們可以定義一個枚舉來存放我們的業務異常資訊。
- 定義一個枚舉類
public enum ExceptionEnum {
BUSINESS_NOT_ONE("400","id為1的資料不支援查詢"),
ERR_NOT_LOOK("401","長得太帥不讓看")
// 往後面累加...
;
private String code;
private String desc;
ExceptionEnum(String code, String desc) {
this.code = code;
this.desc = desc;
}
public void ThrowException(){
ThrowException(code,desc);
}
public void ThrowException(String errMassage){
errMassage = desc +":"+errMassage;
ThrowException(code,errMassage);
}
private BusinessException ThrowException(String code,String desc){
throw new BusinessException(code,desc);
}
}
- 在service 中抛出枚舉異常
@Override
public String getUserByIdBusinessExceptionByEnumOne(String id) {
User user = userMapper.selectById(id);
// 模拟業務異常
if("1".equals(id)){
ExceptionEnum.BUSINESS_NOT_ONE.ThrowException();
}
return user.toString();
}
@Override
public String getUserByIdBusinessExceptionByEnumTwo(String id) {
User user = userMapper.selectById(id);
// 模拟業務異常
if("look".equals(id)){
// 可以動态拼接異常資訊
ExceptionEnum.ERR_NOT_LOOK.ThrowException("你說對吧"+id);
}
return user.toString();
}
- 前台傳回
{
"code": "400",
"massage": "id為1的資料不支援查詢",
"date": null
}
{
"code": "401",
"massage": "長得太帥不讓看:你說對吧look",
"date": null
}
這種做法的好處就是友善管理,一眼就知道自己項目中有多少錯誤,多少異常。但是也會有同學覺得這樣寫好費勁,每次抛異常的時候還要先在枚舉java教程類中定義。是以這個做法是項目跟成員而定。
以上是本人拙見,若有不合适之處,歡迎留言指正。