新聞中心
前言
今天這篇比較簡(jiǎn)單~。日常工作中,我們開(kāi)發(fā)接口時(shí),一般都會(huì)涉及到參數(shù)校驗(yàn)、異常處理、封裝結(jié)果返回等處理。

如果每個(gè)后端開(kāi)發(fā)在參數(shù)校驗(yàn)、異常處理等都是各寫各的,沒(méi)有統(tǒng)一處理的話,代碼就不優(yōu)雅,也不容易維護(hù)。所以,作為一名合格的后端開(kāi)發(fā)工程師,我們需要統(tǒng)一校驗(yàn)參數(shù),統(tǒng)一異常處理、統(tǒng)一結(jié)果返回,讓代碼更加規(guī)范、可讀性更強(qiáng)、更容易維護(hù)。
1. 使用注解,統(tǒng)一參數(shù)校驗(yàn)
假設(shè)小田螺實(shí)現(xiàn)一個(gè)注冊(cè)用戶的功能,在controller 層,他會(huì)先進(jìn)行校驗(yàn)參數(shù),如下:
@RestController
@RequestMapping
public class UserController {
@RequestMapping("addUser")
public String addUser(UserParam userParam) {
if (StringUtils.isEmpty(userParam.getUserName())) {
return "用戶名不能為空";
}
if (StringUtils.isEmpty(userParam.getPhone())) {
return "手機(jī)號(hào)不能為空";
}
if (userParam.getPhone().length() > 11) {
return "手機(jī)號(hào)不能超過(guò)11";
}
if (StringUtils.isEmpty(userParam.getEmail())) {
return "郵箱不能為空";
}
//省略其他參數(shù)校驗(yàn)
//todo 插入用戶信息表
return "SUCCESS";
}
}
以上代碼有什么問(wèn)題嘛?其實(shí)沒(méi)什么問(wèn)題,就是校驗(yàn)有點(diǎn)辣眼睛。正常的添加用戶業(yè)務(wù)還沒(méi)寫,參數(shù)校驗(yàn)就一大堆啦。假設(shè)后來(lái),小田螺又接了一個(gè)需求:編輯用戶信息。實(shí)現(xiàn)編輯用戶信息前,也是先校驗(yàn)信息,如下:
@RequestMapping("editUser")
public String editUser(UserParam userParam) {
if (StringUtils.isEmpty(userParam.getUserName())) {
return "用戶名不能為空";
}
if (StringUtils.isEmpty(userParam.getPhone())) {
return "手機(jī)號(hào)不能為空";
}
if (userParam.getPhone().length() > 11) {
return "手機(jī)號(hào)不能超過(guò)11";
}
if (StringUtils.isEmpty(userParam.getEmail())) {
return "郵箱不能為空";
}
//省略其他參數(shù)校驗(yàn)
//todo 編輯用戶信息表
return "SUCCESS";
}我們可以使用注解的方式,來(lái)進(jìn)行參數(shù)校驗(yàn),這樣代碼更加簡(jiǎn)潔,也方便統(tǒng)一管理。實(shí)際上, spring
boot有個(gè)validation的組件,我們可以拿來(lái)即用。引入這個(gè)包即可:
org.springframework.boot
spring-boot-starter-validation
引入包后,參數(shù)校驗(yàn)就非常簡(jiǎn)潔啦,如下:
public class UserParam {
@NotNull(message = "用戶名不能為空")
private String userName;
@NotNull(message = "手機(jī)號(hào)不能為空")
@Max(value = 11)
private String phone;
@NotNull(message = "郵箱不能為空")
private String email;然后在UserParam參數(shù)對(duì)象中,加入@Validated注解哈,把錯(cuò)誤信息接收到BindingResult對(duì)象,代碼如下:
@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)對(duì)象返回
如果你在你們項(xiàng)目代碼中,看到controller 層報(bào)文返回結(jié)果,有這樣的:
@RequestMapping("/hello")
public String getStr(){
return "hello,撿田螺的小男孩";
}
//返回
hello,撿田螺的小男孩也有這樣的:
@RequestMapping("queryUser")
public UserVo queryUser(String userId) {
return new UserVo("666", "撿田螺的小男孩");
}
//返回:
{"userId":"666","name":"撿田螺的小男孩"}顯然,如果接口返回結(jié)果不統(tǒng)一,前端處理就不方便,我們代碼也不好維護(hù)。再比如小田螺喜歡用Result處理結(jié)果,大田螺喜歡用Response處理結(jié)果,可以想象一下,這些代碼有多亂。
所以作為后端開(kāi)發(fā),我們項(xiàng)目的響應(yīng)結(jié)果,需要統(tǒng)一標(biāo)準(zhǔn)的返回格式。一般一個(gè)標(biāo)準(zhǔn)的響應(yīng)報(bào)文對(duì)象,都有哪些屬性呢?
- 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;
}
}因?yàn)榉祷氐臄?shù)據(jù)類型不是確定的,我們可以使用泛型,如下:
/**
* @author 撿田螺的小男孩
* @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 staticBaseResponse success(T data) {
BaseResponseresponse= new BaseResponse<>();
response.setCode(CodeEnum.SUCCESS.getCode());
response.setMessage(CodeEnum.SUCCESS.getMessage());
response.setData(data);
return response;
}
/**
* 失敗返回
* @param code
* @param message
* @param
* @return
*/
public staticBaseResponse fail(String code, String message) {
BaseResponseresponse = 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,撿田螺的小男孩");
}
//output
{"code":"0000","message":"操作成功","data":"hello,撿田螺的小男孩"}
@RequestMapping("queryUser")
public BaseResponse queryUser(String userId) {
return BaseResponse.success(new UserVo("666", "撿田螺的小男孩"));
}
//output
{"code":"0000","message":"操作成功","data":{"userId":"666","name":"撿田螺的小男孩"}} 3. 統(tǒng)一異常處理
日常開(kāi)發(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 層,很可能會(huì)有類似代碼:
@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());
} 這塊代碼,沒(méi)什么問(wèn)題哈,但是如果try...catch太多,不是很優(yōu)雅。
可以借助注解@RestControllerAdvice,讓代碼更優(yōu)雅。@RestControllerAdvice是一個(gè)應(yīng)用于Controller層的切面注解,它一般配合@ExceptionHandler注解一起使用,作為項(xiàng)目的全局異常處理。我們來(lái)看下demo代碼哈。
還是原來(lái)的UserController,和一個(gè)會(huì)拋出異常的userService的方法,如下:
@RestController
public class UserController {
@Autowired
private UserService userService;
@RequestMapping("/query")
public BaseResponsequeryUserInfo1(UserParam userParam) {
return BaseResponse.success(userService.queryUserInfo(userParam));
}
}
@Service
public class UserServiceImpl implements UserService {
//拋出異常
@Override
public UserVo queryUserInfo(UserParam userParam) throws BizException {
throw new BizException("6666", "測(cè)試異常類");
}
}
我們?cè)俣x一個(gè)全局異常處理器,用@RestControllerAdvice注解,如下:
@RestControllerAdvice(annotations = RestController.class)
public class ControllerExceptionHandler {
}
我們有想要攔截的異常類型,比如想攔截BizException類型,就新增一個(gè)方法,使用@ExceptionHandler注解修飾,如下:
@RestControllerAdvice(annotations = RestController.class)
public class ControllerExceptionHandler {
@ExceptionHandler(BizException.class)
@ResponseBody
public BaseResponsehandler(BizException e) {
System.out.println("進(jìn)入業(yè)務(wù)異常"+e.getRetCode()+e.getRetMessage());
return BaseResponse.fail(CodeEnum.ERROR.getCode(), CodeEnum.ERROR.getMessage());
}
}
嘮叨幾句
本文大家學(xué)到了哪些知識(shí)呢?
- 為了寫出更優(yōu)雅、更簡(jiǎn)潔、更容易維護(hù)的代碼,我們需要統(tǒng)一參數(shù)校驗(yàn)、統(tǒng)一響應(yīng)對(duì)象返回、統(tǒng)一異常處理。
- 參數(shù)校驗(yàn)更簡(jiǎn)潔,可以使用注解實(shí)現(xiàn)。
- 如何統(tǒng)一響應(yīng)對(duì)象返回,一般要包括狀態(tài)碼、描述信息、返回?cái)?shù)據(jù)。
- Controller層如何統(tǒng)一全局異常處理?@RestControllerAdvice+@ExceptionHandler。
- 進(jìn)階篇?大家可以自己實(shí)現(xiàn)自定義注解哈,也建議去看看@RestControllerAdvice實(shí)現(xiàn)原理,它其實(shí)就是一個(gè)切面注解,看下它的源碼即可。
當(dāng)前標(biāo)題:后端思維篇:統(tǒng)一參數(shù)校驗(yàn)、異常處理、結(jié)果返回
標(biāo)題網(wǎng)址:http://fisionsoft.com.cn/article/cdgojch.html


咨詢
建站咨詢
