學而時習之,不亦說乎
前言
對於一個由后端提供的接口來說,有一個統一的響應格式,方便入參校驗,統一的異常處理,是必不可少的,今天我們將這三個基礎功能集成到項目中,使項目更貼近實際的開發場景。
統一響應
在項目開發中,一般返回給前端的都會是一個統一的返回響應對象,因此后端需要封裝一個泛型類來作為響應對象,這樣做的好處是前后端能統一接口返回,可以做規范的響應處理。
實現步驟:
- 創建mingx-common微服務,用於處理公共業務,包括統一響應,統一異常攔截,工具類等。
- 在src/mian/java下新建com.mingx.common包,在此包下面創建
AppResult.java,AppResultBuilder.java,ResultCode.java文件,作用分別如下:
- AppResult.java:規范返回結果的格式的類
package com.mingx.common;
public class AppResult<T> {
private int code;
private String msg;
private T data;// 數據
public int getCode() {
return code;
}
public void setCode(int code) {
this.code = code;
}
public String getMsg() {
return msg;
}
public void setMsg(String msg) {
this.msg = msg;
}
public T getData() {
return data;
}
public void setData(T data) {
this.data = data;
}
}
- AppResultBuilder.java:方便返回格式組裝的類
package com.mingx.common;
public class AppResultBuilder {
private static Integer successCode = ResultCode.SUCCESS.getCode();
private static String successMsg = ResultCode.SUCCESS.getMsg();
//成功,不返回具體數據
public static AppResult<String> success(){
AppResult<String> result = new AppResult<String>();
result.setCode(successCode);
result.setMsg(successMsg);
result.setData("");
return result;
}
//成功,返回數據
public static <T> AppResult<T> success(T t){
AppResult<T> result = new AppResult<T>();
result.setCode(successCode);
result.setMsg(successMsg);
result.setData(t);
return result;
}
//失敗,返回失敗信息
public static <T> AppResult<T> error(ResultCode code){
AppResult<T> result = new AppResult<T>();
result.setCode(code.getCode());
result.setMsg(code.getMsg());
return result;
}
//失敗,返回失敗信息
public static <T> AppResult<T> error(ResultCode code,String extraMsg){
AppResult<T> result = new AppResult<T>();
result.setCode(code.getCode());
result.setMsg(code.getMsg() + "," + extraMsg);
return result;
}
//失敗,返回失敗信息
public static <T> AppResult<T> error(Integer code,String extraMsg){
AppResult<T> result = new AppResult<T>();
result.setCode(code);
result.setMsg(extraMsg);
return result;
}
}
- ResultCode.java:枚舉類,定義返回信息
package com.mingx.common;
public enum ResultCode {
/* 成功狀態碼 */
SUCCESS(10000, "success"),
/* 系統錯誤: */
SYSTEM_ERROR(10001, "系統繁忙,請稍后重試"),
/* 參數錯誤: */
PARAM_ERROR(10002, "參數有誤"),
/* 非法登錄:*/
ILLEGAL_ERROR(10003, "用戶非法登錄"),
/* 用戶模塊:20001-29999*/
USER_NOT_LOGGED_IN(20001, "用戶未登錄"),
USER_LOGIN_ERROR(20002, "用戶名或者密碼錯誤,請檢查重試"),
USER_ACCOUNT_FORBIDDEN(20003, "賬號已被禁用"),
USER_HAS_EXISTED(20004, "用戶已存在");
private Integer code;
private String msg;
ResultCode(Integer code, String msg) {
this.code = code;
this.msg = msg;
}
public Integer getCode() {
return code;
}
public String getMsg() {
return msg;
}
}
入參校驗
平時項目中,難免需要對參數 進行一些參數正確性的校驗,這些校驗出現在業務代碼中,讓我們的業務代碼顯得臃腫,而且,頻繁的編寫這類參數校驗代碼很無聊。鑒於此,覺得 Hibernate Validator 框架剛好解決了這些問題,可以很優雅的方式實現參數的校驗,讓業務代碼 和 校驗邏輯 分開,不再編寫重復的校驗邏輯。
Hibernate Validator 是 Bean Validation 的參考實現 . Hibernate Validator 提供了 JSR 303 規范中所有內置 constraint 的實現,除此之外還有一些附加的 constraint。
Bean Validation 為 JavaBean 驗證定義了相應的元數據模型和API。缺省的元數據是 Java Annotations,通過使用 XML 可以對原有的元數據信息進行覆蓋和擴展。Bean Validation 是一個運行時的數據驗證框架,在驗證之后驗證的錯誤信息會被馬上返回。
准備步驟:
- 在父pom中增加hibernate validator主版本管理
<hibernate-validator.version>6.0.14.Final</hibernate-validator.version>
<!-- hibernate validator主版本管理 -->
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-validator</artifactId>
<version>${hibernate-validator.version}</version>
</dependency>
- 在src/mian/java下新建com.mingx.common包,在此包下面創建RegexpContants接口,用於常用正則表達式管理,根據項目中的實際情況添加。
package com.mingx.common;
/**
* 通用正則表達式
*
* @author Admin
*/
public interface RegexpContants {
/**
* 可空標記
*/
String NULLFLAG = "^$|";
/**
* 手機正則表達式
*/
String MOBIL_EREGEXP = "^(13|14|15|16|17|18|19)\\d{9}$";
/**
* 郵箱正則表達式
*/
String EMAIL_EREGEXP = "^\\s*\\w+(?:\\.{0,1}[\\w-]+)*@[a-zA-Z0-9]+(?:[-.][a-zA-Z0-9]+)*\\.[a-zA-Z]+\\s*$";
/**
* 身份證正則表達式
*/
String ID_CARD_EREGEXP = "(^\\d{15}$)|(^\\d{18}$)|(^\\d{17}(\\d|X|x)$)";
/**
* 固話正則表達式
*/
String TELEPHONE_EREGEXP = "^(0\\d{2,3}-)?\\d{7,8}(-\\d{3,4})?$";
/**
* 網站正則表達式
*/
String URL_EREGEXP = "(https?|ftp|file)://[-A-Za-z0-9+&@#/%?=~_|!:,.;]+[-A-Za-z0-9+&@#/%=~_|]";
/**
* 6位短信驗證碼
*/
String SIXNUMBER_EREGEXP = "^\\d{6}$";
/**
* 4位短信驗證碼
*/
String FOURNUMBER_EREGEXP = "^\\d{4}$";
/**
* 驗證數字
*/
String NUMBER_EREGEXP = "^\\d{1,}$";
/**
* 年齡
*/
String AGE_EREGEXP = "^[0-9]{1,2}$";
/**
* 密碼(6-12位字母或數字)正則表達式
*/
String PASSWORD_OR_EREGEXP = "^[0-9A-Za-z]{6,12}$";
/**
* 密碼(6-12位字母和數字)正則表達式
*/
String PASSWORD_AND_EREGEXP = "^(?![0-9]+$)(?![a-zA-Z]+$)[0-9A-Za-z]{6,12}$";
/**
* 中文姓名正則表達式
*/
String CHINESE_NAME_EREGEXP = "^[\u4e00-\u9fa5]+(\\·[\u4e00-\u9fa5]+)*$";
/**
* 金額正則表達式 正整數,不能為小數或者負數
*/
String MONEY_EREGEXP = "^([1-9]\\d*)*$";
/**
* 只能輸入數字 0 或 1
**/
String ZERO_OR_ONE_EREGEXP = "^[0-1]{1}$";
/**
* 正整數
*/
String POSITIVE_NUMBER = "^[0-9]*[1-9][0-9]*$";
/**
* 年月日日期模式
*/
String SIMPLE_DATE_PATTERN = "^[1-9]\\d{3}-(0[1-9]|1[0-2])-(0[1-9]|[1-2][0-9]|3[0-1])$";
/**
* 年月日日期模式無-
*/
String SIMPLE_DATE_PATTERN_SIMPLE = "^[1-9]\\d{3}(0[1-9]|1[0-2])(0[1-9]|[1-2][0-9]|3[0-1])$";
/**
* 日期格式驗證
*/
String DATE_FORMATE_TEMPLATE1_EREGEXP = "^[1-2][0-9][0-9][0-9]-([1][0-2]|0?[1-9])-([12][0-9]|3[01]|0?[1-9]) ([01][0-9]|[2][0-3]):[0-5][0-9]$";
/**
* 郵編格式驗證
*/
String POSTCODE_EREGEXP = "^[0-9]{6}$";
}
在后續的實際運用中在具體看如何使用。
統一異常響應
- 在mingx-common的pom中引入spring-boot-starter-web依賴
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>com.mingx</groupId>
<artifactId>mingx-demo</artifactId>
<version>0.0.1-SNAPSHOT</version>
</parent>
<groupId>com.mingx.common</groupId>
<artifactId>mingx-common</artifactId>
<packaging>jar</packaging>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
</dependencies>
</project>
- 在src/mian/java下新建com.mingx.common,hander包,在此包下面創建SysExceptionHandler.java
利用ControllerAdvice注解實現全局的異常處理,首先是針對入參校驗的異常處理,會拋出MethodArgumentNotValidException的異常,首先被捕獲,返回統一的參數有誤響應。接下來捕獲所有Exception的異常,此處異常其實可以再細化,現在為了模擬,統一捕獲Exception異常,返回統一的【系統繁忙,請稍后重試】響應。
package com.mingx.common.hander;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;
import com.mingx.common.AppResult;
import com.mingx.common.AppResultBuilder;
import com.mingx.common.ResultCode;
@ControllerAdvice
@Component
public class SysExceptionHandler {
private static final Logger logger = LoggerFactory.getLogger(SysExceptionHandler.class);
/**
* 入參校驗
* @param exception
* @return
*/
@ResponseBody
@ExceptionHandler(MethodArgumentNotValidException.class)
public AppResult<String> handle(MethodArgumentNotValidException exception) {
String message = exception.getBindingResult().getAllErrors().get(0).getDefaultMessage();
return AppResultBuilder.error(ResultCode.PARAM_ERROR,message);
}
/**
* 全局異常捕捉處理
* @param ex
* @return
*/
@ResponseBody
@ExceptionHandler(value = Exception.class)
public AppResult<String> errorHandler(Exception ex) {
logger.error(ex.getMessage(),ex);
return AppResultBuilder.error(ResultCode.SYSTEM_ERROR);
}
}
綜合實踐
做好前面的三個基本功能的實現准備后,現在模擬實現保存用戶信息的一個功能,步驟如下:
- 在項目pom中引入mingx-common、hibernate-validator依賴
<dependency>
<groupId>com.mingx.common</groupId>
<artifactId>mingx-common</artifactId>
<version>0.0.1-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-validator</artifactId>
</dependency>
- 在mingx-user-pojo項目中創建com.mingx.user.bo包,該包專門放校驗入參的pojo類,創建SaveUserBO.java類,建議BO類的取名方式為:對應方法名 + BO,具體代碼如下:
package com.mingx.user.bo;
import javax.validation.constraints.NotBlank;
import javax.validation.constraints.Pattern;
import org.hibernate.validator.constraints.Length;
import com.mingx.common.RegexpContants;
import lombok.Data;
@Data
public class SaveUserBO {
@NotBlank(message = "用戶名稱不能為空")
@Length(max = 10,message = "長度不能超過10個字符")
private String name;
@NotBlank(message = "性別不能為空")
@Pattern(regexp = RegexpContants.ZERO_OR_ONE_EREGEXP,message = "性別格式不正確")
private String sex;
@NotBlank(message = "出生日期不能為空")
@Pattern(regexp = RegexpContants.SIMPLE_DATE_PATTERN_SIMPLE,message = "出生日期格式不正確")
private String birthday;
}
@NotBank注解即是非空注解,標識該字段不能為空
@Length注解可設置其字段長度
@Pattern注解可進行自定義正則表達式校驗,可以將相關正則表達式單獨抽取出來,利用之前創建的RegexpContants.java接口,其中定義常用正則表達式即可
其他更多校驗方面的注解可參考hibernate Validator官方給出的參考文檔,這里僅是最簡單的使用。
- 在mingx-user項目的SysController.java中,增加保存用戶信息接口:
注意:接口入參需要添加 @Validated 注解,才會對入參進行參數校驗
@PostMapping("/saveUser")
public AppResult<String> saveUser(@Validated @RequestBody SaveUserBO bo){
Integer count = sysUserService.count(new QueryWrapper<SysUser>().eq("name", bo.getName()));
if(count > 0) {
return AppResultBuilder.error(ResultCode.USER_HAS_EXISTED);
}
SysUser user = new SysUser();
user.setName(bo.getName()).setSex(Integer.valueOf(bo.getSex()))
.setBirthday(LocalDate.parse(bo.getBirthday(),DateTimeFormatter.ofPattern("yyyy-MM-dd")))
.setCreateTime(LocalDateTime.now());
sysUserService.save(user);
return AppResultBuilder.success(user.getId());
}
- 啟動nacos,啟動mingx-user,打開postman,發送post請求,訪問
http://localhost:7001/user/saveUser,
在body中加入json格式的參數,可如下:

保存成功,則如下顯示:

如果某字段為空,或者格式不正確,則會如下顯示:

由於增加了重復的邏輯判斷,再次保存,會返回:

結束語
本章未涉及到SpringClould相關的知識點,但是只要是一個合格的后端接口,都必須要這最基礎的三點功能,簡單在項目中實踐,做優化提煉,有助於技術層次的提高,不能一頭就扎到業務開發中,忽略了這些基礎功能實現的過程。
下一章我們學習使用SpringcClould Feign,一個棒棒噠聲明式偽RPC的REST客戶端。
點關注,不迷路
微信搜索【尋的足跡】關注公眾號,第一時間收到最新文章
![]()