后端
常用依赖
Lombok
<dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <version>1.18.36</version> <optional>true</optional></dependency>Hutool工具类 常见的Java工具类库,包含了常用的工具类,例如字符串处理、日期处理、文件处理、文件加密等等。它节省了开发人员对项目中公用类和公用工具方法的封装时间。
<dependency> <groupId>cn.hutool</groupId> <artifactId>hutool-all</artifactId> <version>5.8.38</version></dependency>Knife4j Knife4j 是基于Swagger接口文档的增强工具,提供了更加友好的API文档界面和功能扩展,例如动态参数调试、分组文 档等。它适合用于Spring Boot项目中,能够通过简单的配置自动生成接口文档,让开发者和前端快速了解和调试接口, 提高协作效率。
<dependency> <groupId>com.github.xiaoymin</groupId> <artifactId>knife4j-openapi3-jakarta-spring-boot-starter</artifactId> <version>4.4.0</version></dependency>AOP切面
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-aop</artifactId></dependency>使用@EnableAspectJAutoProxy(exposeProxy = true): 通过Spring AOP提供当前代理对象的访问,可以在业务逻辑中访问当前的代理对象。
通用基础代码
统一异常错误码
统一错误码,便于前端处理。
@Getterpublic 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; }}自定义异常
继承RuntimeException异常,可以实现自己的异常处理
@Getterpublic 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(); }}响应包装类
@Datapublic 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()); }}通用返回响应类
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); }}全局异常处理器
使用AOP拦截异常
@Hidden@RestControllerAdvice@Slf4jpublic 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, "系统错误"); }}请求包装类
@Datapublic class DeleteRequest implements Serializable {
/** * id */ private Long id;
private static final long serialVersionUID = 1L;}@Datapublic class PageRequest {
/** * 当前页号 */ private int pageNum = 1;
/** * 页面大小 */ private int pageSize = 10;
/** * 排序字段 */ private String sortField;
/** * 排序顺序(默认降序) */ private String sortOrder = "descend";}全局跨域配置
@Configurationpublic class CorsConfig implements WebMvcConfigurer {
@Override public void addCorsMappings(CorsRegistry registry) { // 覆盖所有请求 registry.addMapping("/**") // 允许发送 Cookie .allowCredentials(true) // 放行哪些域名(必须用 patterns,否则 * 会和 allowCredentials 冲突) .allowedOriginPatterns("*") .allowedMethods("GET", "POST", "PUT", "DELETE", "OPTIONS") .allowedHeaders("*") .exposedHeaders("*"); }}yml配置
spring: application: name: fly-geniusserver: port: 8100 servlet: context-path: /api
# 文档springdoc: api-docs: enabled: true group-configs: - group: 'default' paths-to-match: - '\**' packages-to-scan: - com.flycode.flygenius.controllerknife4j: enable: true setting: language: zh_cn前端配置
全局自定义请求
需要自定义全局请求地址等,参考Axios官方文档,编写请求配置文件request.ts。包括全局接口请求地址、超时时间、自定义请求响应拦截器等。
响应拦截器的应用场景:我们需要对接口的通用响应 进行统一处理,比如从response中取出data;或者根据code去集 中处理错误。这样不用在每个接口请求中都去写相同的逻辑。
比如可以在全局响应拦截器中,读取出结果中的data,并校验code是否合法,如果是未登录状态,则自动登录。
示例代码如下,其中 withCredentials: true 一定要写,否则无法在发请求时携带Cookie,就无法完成登录。
npm install axiosimport axios from 'axios'import { message } from 'ant-design-vue'
// 创建 Axios 实例const myAxios = axios.create({ baseURL: 'http://localhost:8100/api', timeout: 60000, withCredentials: true,})
// 全局请求拦截器myAxios.interceptors.request.use( function (config) { // Do something before request is sent return config }, function (error) { // Do something with request error return Promise.reject(error) },)
// 全局响应拦截器myAxios.interceptors.response.use( function (response) { const { data } = response // 未登录 if (data.code === 40100) { // 不是获取用户信息的请求,并且用户目前不是已经在用户登录页面,则跳转到登录页面 if ( !response.request.responseURL.includes('user/get/login') && !window.location.pathname.includes('/user/login') ) { message.warning('请先登录') window.location.href = `/user/login?redirect=${window.location.href}` } } return response }, function (error) { // Any status codes that falls outside the range of 2xx cause this function to trigger // Do something with response error return Promise.reject(error) },)
export default myAxios自动生成请求代码
安装openapi
npm i --save-dev @umijs/openapinpm i --save-dev tslib创建openapi2ts.config.ts,配置后端地址和生成文件位置
export default { requestLibPath: "import request from '@/request'", schemaPath: 'http://localhost:8100/api/v3/api-docs', serversPath: './src',}需要确保package和openapi2ts.config.ts在同一个目录下,不然需要加上对应的文件夹 package.json添加
"openapi2ts": "openapi2ts"如果是ts环境,生成的代码会报错,但是不影响运行。可以在tsconfig.json中修改
{ "files": [], "compilerOptions": { "target": "esnext", "module": "esnext", "moduleResolution": "node", "strict": true, "jsx": "preserve", "sourceMap": true, "resolveJsonModule": true, "esModuleInterop": true, "skipLibCheck": true, "baseUrl": ".", "paths": { "@/*": [ "src/*" ] } }, "include": [ "src/**/*.ts", "src/**/*.tsx", "src/**/*.vue" ], "references": [ { "path": "./tsconfig.node.json" }, { "path": "./tsconfig.app.json" } ]}
评论