后端通用基礎代碼
通用基礎代碼是指:“無論在任何后端項目中,都可以復用的代碼。這種代碼一般 “一輩子只用寫一次” ,了解作用之后復制粘貼即可,無需記憶。
目錄結構如下:
1、自定義異常
自定義錯誤碼,對錯誤進行收斂,便于前端統一處理。
這里有兩個小技巧:
- 自定義錯誤碼時,建議跟主流的錯誤碼(比如 HTTP 錯誤碼)的含義保持一致,比如 “未登錄” 定義為 40100,和 HTTP 401 錯誤(用戶需要進行身份認證)保持一致,會更容易理解。
- 錯誤碼不要完全連續,預留一些間隔,便于后續擴展。
在 exception
包下新建錯誤碼枚舉類:
import lombok.Getter;@Getter
public enum ErrorCode {SUCCESS(0, "ok"),PARAMS_ERROR(40000, "請求參數錯誤"),NOT_LOGIN_ERROR(40100, "未登錄"),NO_AUTH_ERROR(40101, "無權限"),NOT_FOUND_ERROR(40400, "請求數據不存在"),FORBIDDEN_ERROR(40300, "禁止訪問"),SYSTEM_ERROR(50000, "系統內部異常"),OPERATION_ERROR(50001, "操作失敗");/*** 狀態碼*/private final int code;/*** 信息*/private final String message;ErrorCode(int code, String message) {this.code = code;this.message = message;}}
一般不建議直接拋出 Java 內置的 RuntimeException,而是自定義一個業務異常,和內置的異常類區分開,便于定制化輸出錯誤信息:
在 exception
包下新建業務異常類
import lombok.Getter;@Getter
public class BusinessException extends RuntimeException {/*** 錯誤碼*/private final int code;public BusinessException(int code, String message) {super(message);this.code = code;}public BusinessException(ErrorCode errorCode) {super(errorCode.getMessage());this.code = errorCode.getCode();}public BusinessException(ErrorCode errorCode, String message) {super(message);this.code = errorCode.getCode();}}
為了更方便地根據情況拋出異常,可以封裝一個 ThrowUtils,類似斷言類,簡化拋異常的代碼:
在 exception
包下新建 ThrowUtils 類
public class ThrowUtils {/*** 條件成立則拋異常** @param condition 條件* @param runtimeException 異常*/public static void throwIf(boolean condition, RuntimeException runtimeException) {if (condition) {throw runtimeException;}}/*** 條件成立則拋異常** @param condition 條件* @param errorCode 錯誤碼*/public static void throwIf(boolean condition, ErrorCode errorCode) {throwIf(condition, new BusinessException(errorCode));}/*** 條件成立則拋異常** @param condition 條件* @param errorCode 錯誤碼* @param message 錯誤信息*/public static void throwIf(boolean condition, ErrorCode errorCode, String message) {throwIf(condition, new BusinessException(errorCode, message));}
}
2、響應包裝類
一般情況下,每個后端接口都要返回調用碼、數據、調用信息等,前端可以根據這些信息進行的處理。
我們可以封裝統一的響應結果類,便于前端統一獲取這些信息。
通用響應類:
新建 common
包
在 common
包新建響應包裝類
import lombok.Data;import java.io.Serializable;@Data
public class BaseResponse<T> implements Serializable {private int code;private T data;private String message;public BaseResponse(int code, T data, String message) {this.code = code;this.data = data;this.message = message;}public BaseResponse(int code, T data) {this(code, data, "");}public BaseResponse(ErrorCode errorCode) {this(errorCode.getCode(), null, errorCode.getMessage());}
}
但之后每次接口返回值時,都要手動 new 一個 BaseResponse 對象并傳入參數,比較麻煩,我們可以新建一個工具類,提供成功調用和失敗調用的方法,支持靈活地傳參,簡化調用。
在 common
包新建工具類
public class ResultUtils {/*** 成功** @param data 數據* @param <T> 數據類型* @return 響應*/public static <T> BaseResponse<T> success(T data) {return new BaseResponse<>(0, data, "ok");}/*** 失敗** @param errorCode 錯誤碼* @return 響應*/public static BaseResponse<?> error(ErrorCode errorCode) {return new BaseResponse<>(errorCode);}/*** 失敗** @param code 錯誤碼* @param message 錯誤信息* @return 響應*/public static BaseResponse<?> error(int code, String message) {return new BaseResponse<>(code, null, message);}/*** 失敗** @param errorCode 錯誤碼* @return 響應*/public static BaseResponse<?> error(ErrorCode errorCode, String message) {return new BaseResponse<>(errorCode.getCode(), null, message);}
}
3、全局異常處理器
為了防止意料之外的異常,利用 AOP 切面全局對業務異常和 RuntimeException 進行捕獲:
在 exception
包下新建全局異常處理器類
package com.ping.aiagent.exception;import com.ping.aiagent.common.BaseResponse;
import com.ping.aiagent.common.ResultUtils;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;@RestControllerAdvice
@Slf4j
public class GlobalExceptionHandler {@ExceptionHandler(BusinessException.class)public BaseResponse<?> businessExceptionHandler(BusinessException e) {log.error("BusinessException", e);return ResultUtils.error(e.getCode(), e.getMessage());}@ExceptionHandler(RuntimeException.class)public BaseResponse<?> runtimeExceptionHandler(RuntimeException e) {log.error("RuntimeException", e);return ResultUtils.error(ErrorCode.SYSTEM_ERROR, "系統錯誤");}
}
4、請求包裝類
對于 “分頁” 、“刪除某條數據” 這類通用的請求,可以封裝統一的請求包裝類,用于接受前端傳來的參數,之后相同參數的請求就不用專門再新建一個類了。
分頁請求包裝類,接受頁號、頁面大小、排序字段、排序順序參數:
在 common
包新建分頁請求包裝類
import lombok.Data;@Data
public class PageRequest {/*** 當前頁號*/private int current = 1;/*** 頁面大小*/private int pageSize = 10;/*** 排序字段*/private String sortField;/*** 排序順序(默認降序)*/private String sortOrder = "descend";
}
刪除請求包裝類,接受要刪除的 id 作為參數:
在 common
包新建刪除請求包裝類
import lombok.Data;import java.io.Serializable;@Data
public class DeleteRequest implements Serializable {/*** id*/private Long id;private static final long serialVersionUID = 1L;
}
5、全局跨域配置
跨域是指瀏覽器訪問的 URL(前端地址)和后端接口地址的域名(或端口號)不一致導致的,瀏覽器為了安全,默認禁止跨域請求訪問。
為了開發調試方便,我們可以通過全局跨域配置,讓整個項目所有的接口支持跨域,解決跨域報錯。
新建 config 包,用于存放所有的配置相關代碼。全局跨域配置代碼如下:
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.CorsRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;@Configuration
public class CorsConfig implements WebMvcConfigurer {@Overridepublic void addCorsMappings(CorsRegistry registry) {// 覆蓋所有請求registry.addMapping("/**")// 允許發送 Cookie.allowCredentials(true)// 放行哪些域名(必須用 patterns,否則 * 會和 allowCredentials 沖突).allowedOriginPatterns("*").allowedMethods("GET", "POST", "PUT", "DELETE", "OPTIONS").allowedHeaders("*").exposedHeaders("*");}
}