日常工作中,我們開發(fā)接口時,一般都會涉及到參數(shù)校驗、異常處理、封裝結(jié)果返回等處理。如果每個后端開發(fā)在參數(shù)校驗、異常處理等都是各寫各的,沒有統(tǒng)一處理的話,代碼就不優(yōu)雅,也不容易維護(hù)。所以,作為一名合格的后端開發(fā)工程師,我們需要統(tǒng)一校驗參數(shù),統(tǒng)一異常處理、統(tǒng)一結(jié)果返回,讓代碼更加規(guī)范、可讀性更強(qiáng)、更容易維護(hù)。
- 使用注解,優(yōu)雅進(jìn)行參數(shù)校驗
- 統(tǒng)一結(jié)果返回
- 統(tǒng)一異常處理
- 嘮叨幾句
1. 使用注解,統(tǒng)一參數(shù)校驗
假設(shè)實現(xiàn)一個注冊用戶的功能,在controller 層,他會先進(jìn)行校驗參數(shù),如下:
@RestController@RequestMappingpublic class UserController { @RequestMapping(“addUser”) public String addUser(UserParam userParam) { if (StringUtils.isEmpty(userParam.getUserName())) { return “用戶名不能為空”; } if (StringUtils.isEmpty(userParam.getPhone())) { return “手機(jī)號不能為空”; } if (userParam.getPhone().length() > 11) { return “手機(jī)號不能超過11”; } if (StringUtils.isEmpty(userParam.getEmail())) { return “郵箱不能為空”; } //省略其他參數(shù)校驗 //todo 插入用戶信息表 return “SUCCESS”; }}
以上代碼有什么問題嘛?其實沒什么問題,就是校驗有點辣眼睛。正常的添加用戶業(yè)務(wù)還沒寫,參數(shù)校驗就一大堆啦。假設(shè)后來,又接了一個需求:編輯用戶信息。實現(xiàn)編輯用戶信息前,也是先校驗信息,如下:
@RequestMapping(“editUser”)public String editUser(UserParam userParam) { if (StringUtils.isEmpty(userParam.getUserName())) { return “用戶名不能為空”; } if (StringUtils.isEmpty(userParam.getPhone())) { return “手機(jī)號不能為空”; } if (userParam.getPhone().length() > 11) { return “手機(jī)號不能超過11”; } if (StringUtils.isEmpty(userParam.getEmail())) { return “郵箱不能為空”; } //省略其他參數(shù)校驗 //todo 編輯用戶信息表 return “SUCCESS”;}
我們可以使用注解的方式,來進(jìn)行參數(shù)校驗,這樣代碼更加簡潔,也方便統(tǒng)一管理。實際上, spring boot有個validation的組件,我們可以拿來即用。引入這個包即可:
org.springframework.boot spring-boot-starter-validation
引入包后,參數(shù)校驗就非常簡潔啦,如下:
public class UserParam { @NotNull(message = “用戶名不能為空”) private String userName; @NotNull(message = “手機(jī)號不能為空”) @Max(value = 11) private String phone; @NotNull(message = “郵箱不能為空”) private String email;
然后在UserParam參數(shù)對象中,加入@Validated注解哈,把錯誤信息接收到BindingResult對象,代碼如下:
@RequestMapping(“addUser”) public String addUser(@Validated UserParam userParam, BindingResult result) { List fieldErrors = result.getFieldErrors(); if (!fieldErrors.isEmpty()) { return fieldErrors.get(0).getDefaultMessage(); } //todo 插入用戶信息表 return “SUCCESS”; }
2. 接口統(tǒng)一響應(yīng)對象返回
如果你在你們項目代碼中,看到controller 層報文返回結(jié)果,有這樣的:
@RequestMapping(“/hello”)public String getStr(){ return “hello,test”;}//返回hello,test
也有這樣的:
@RequestMapping(“queryUser”)public UserVo queryUser(String userId) { return new UserVo(“666”, “test”);}//返回:{“userId”:”666″,”name”:”test”}
顯然,如果接口返回結(jié)果不統(tǒng)一,前端處理就不方便,我們代碼也不好維護(hù)。再比如有的人喜歡用Result處理結(jié)果,有點人喜歡用Response處理結(jié)果,可以想象一下,這些代碼有多亂。
所以作為后端開發(fā),我們項目的響應(yīng)結(jié)果,需要統(tǒng)一標(biāo)準(zhǔn)的返回格式。一般一個標(biāo)準(zhǔn)的響應(yīng)報文對象,都有哪些屬性呢?
- code :響應(yīng)狀態(tài)碼
- message :響應(yīng)結(jié)果描述
- data:返回的數(shù)據(jù)
響應(yīng)狀態(tài)碼一般用枚舉表示哈:
public enum CodeEnum { /**操作成功**/ SUCCESS(“0000″,”操作成功”), /**操作失敗**/ ERROR(“9999″,”操作失敗”),; /** * 自定義狀態(tài)碼 **/ private String code; /**自定義描述**/ private String message; CodeEnum(String code, String message){ this.code = code; this.message = message; } public String getCode() { return code; } public String getMessage() { return message; }}
因為返回的數(shù)據(jù)類型不是確定的,我們可以使用泛型,如下:
/** * @author kevin * @param */public class BaseResponse { /** * 響應(yīng)狀態(tài)碼(0000表示成功,9999表示失敗 */ private String code; /** * 響應(yīng)結(jié)果描述 */ private String message; /** * 返回的數(shù)據(jù) */ private T data; /** * 成功返回 * @param data * @param * @return */ public static BaseResponse success(T data) { BaseResponse response= new BaseResponse(); response.setCode(CodeEnum.SUCCESS.getCode()); response.setMessage(CodeEnum.SUCCESS.getMessage()); response.setData(data); return response; } /** * 失敗返回 * @param code * @param message * @param * @return */ public static BaseResponse fail(String code, String message) { BaseResponse response = new BaseResponse(); response.setCode(code); response.setMessage(message); return response; } public void setCode(String code) { this.code = code; } public void setMessage(String message) { this.message = message; } public void setData(T data) { this.data = data; }}
有了統(tǒng)一的響應(yīng)體,我們就可以優(yōu)化一下controller 層的代碼啦:
@RequestMapping(“/hello”)public BaseResponse getStr(){ return BaseResponse.success(“hello,test”);}//output{“code”:”0000″,”message”:”操作成功”,”data”:”hello,test”}@RequestMapping(“queryUser”)public BaseResponse queryUser(String userId) { return BaseResponse.success(new UserVo(“666”, “test”));}//output{“code”:”0000″,”message”:”操作成功”,”data”:{“userId”:”666″,”name”:”test”}}
3. 統(tǒng)一異常處理
日常開發(fā)中,我們一般都是自定義統(tǒng)一的異常類,如下:
public class BizException extends RuntimeException { private String retCode; private String retMessage; public BizException() { super(); } public BizException(String retCode, String retMessage) { this.retCode = retCode; this.retMessage = retMessage; } public String getRetCode() { return retCode; } public String getRetMessage() { return retMessage; }}
在controller 層,很可能會有類似代碼:
@RequestMapping(“/query”)public BaseResponse queryUserInfo(UserParam userParam) { try { return BaseResponse.success(userService.queryUserInfo(userParam)); } catch (BizException e) { //doSomething } catch (Exception e) { //doSomething } return BaseResponse.fail(CodeEnum.ERROR.getCode(),CodeEnum.ERROR.getMessage());}
這塊代碼,沒什么問題哈,但是如果try…catch太多,不是很優(yōu)雅。
可以借助注解@RestControllerAdvice,讓代碼更優(yōu)雅。@RestControllerAdvice是一個應(yīng)用于Controller層的切面注解,它一般配合@ExceptionHandler注解一起使用,作為項目的全局異常處理。我們來看下demo代碼哈。
還是原來的UserController,和一個會拋出異常的userService的方法,如下:
@RestControllerpublic class UserController { @Autowired private UserService userService; @RequestMapping(“/query”) public BaseResponse queryUserInfo1(UserParam userParam) { return BaseResponse.success(userService.queryUserInfo(userParam)); }}@Servicepublic class UserServiceImpl implements UserService { //拋出異常 @Override public UserVo queryUserInfo(UserParam userParam) throws BizException { throw new BizException(“6666”, “測試異常類”); }}
我們再定義一個全局異常處理器,用@RestControllerAdvice注解,如下:
@RestControllerAdvice(annotations = RestController.class)public class ControllerExceptionHandler {}
我們有想要攔截的異常類型,比如想攔截BizException類型,就新增一個方法,使用@ExceptionHandler注解修飾,如下:
@RestControllerAdvice(annotations = RestController.class)public class ControllerExceptionHandler { @ExceptionHandler(BizException.class) @ResponseBody public BaseResponse handler(BizException e) { System.out.println(“進(jìn)入業(yè)務(wù)異常”+e.getRetCode()+e.getRetMessage()); return BaseResponse.fail(CodeEnum.ERROR.getCode(), CodeEnum.ERROR.getMessage()); }}