【Mock平台】为系列测试开发教程,从0到1编码带你一步步使用Spring Boot 和 Antd React框架完成搭建一个测试工具平台,希望作为一个实战项目对各位的测试开发学习之路有帮助,关注公众号发送“mock”获取github项目源码地址,大奇一个专注测试技术干货原创与分享的家伙。
上两节我们学习了 HandlerInterceptor 和 WebMvcConfigurer 基础知识,接下我们就可以利用此模块进行拦截服务的逻辑实现了。在此之前我这边先给出一个本项目要实现的一个简化版逻辑处理图,大家可以根据此图先自己构思所要设计数据库表和代码块。
路径匹配数据表
通过创建一个接口基本要素的表来对mock接口进行管理,其中方法、路径,状态码为最基本字段,其他选项则根据需要进行扩展。比如我这里有标签进行一级分类,是否其中,以及在没有规则匹配的默认返回值等。
create table mock_api
(api_id int auto_increment primary key,api_tag_id int null comment '接口标签',api_project_id int null comment '所属项目',api_title varchar(50) null comment '自定义名称',api_method varchar(10) null comment '接口方法',api_path varchar(200) null comment '接口路径',api_enabel tinyint null comment '是否启用',api_desc varchar(100) null comment '描述',api_response_code int null comment '返回状态码',api_response_default text null comment '接口默认值,用户没有规则的默认返回',api_create_user varchar(50) null,api_create_date datetime default CURRENT_TIMESTAMP null,api_update_user varchar(50) null,api_update_date datetime default CURRENT_TIMESTAMP null on update CURRENT_TIMESTAMP
)comment '接口管理表';
表创建完后我们再往里插入两条数据方便后边的接口开发调试。
路径匹配逻辑代码
在之前的分享中已经讲过层级架构和Spring Boot接口请求的基本套路,分别创建实体类和服务类。
实体类
编写数据库表mock_api的字段匹配的实体类。
@Data
@NoArgsConstructor
@AllArgsConstructor
public class QMockApiEntity extends QBaseEntity implements Serializable {private Long id;private Long tagId;private Long projectId;private String title;private String method;private String path;private Integer enable;private String desc;private Integer resCode;private JSONObject resDefault;}
基于返回对象QMockApiEntity在QMockService写个待实现业务处理方法。
本项目中查询返回的是一个list,在后续的逻辑处理根据数据进行判断。其实正常情况下如果前后端实现时,控制了多条件下的不能重复录入,那么这里完全可以只返回单对象。
@Service("QMockService")
public class QMockService{public List<QMockApiEntity> selectApiByPath(String path, String method) {return null;}
}
数据操作
紧接着就是要写个条件查询语句,为了快速实现验证,我们这里先只通过 api_path
和 api_method
两个参数进行条件查询。
因为查询SQL相对简单,这里我们直接用@select
注解完成查询,多加一个@Results
进行数据类型和JAVA中的类型绑定和转换。
@Mapper
public interface QMockApiMapper {@Select("SELECT * from mock_api WHERE api_path = #{path} AND api_method=#{method};")@Results(id = "apiMap",value = {@Result(column = "api_id", property = "id"),@Result(column = "api_tag_id", property="tagId"),@Result(column = "api_project_id", property="projectId"),@Result(column = "api_title", property="title"),@Result(column = "api_method", property = "method"),@Result(column = "api_path", property="path"),@Result(column = "api_enable", property="enable"),@Result(column = "api_desc", property = "desc"),@Result(column = "api_response_code", property="resCode"),@Result(column = "api_response_default", property="resDefault",jdbcType = JdbcType.VARCHAR, typeHandler = JSONObjectTypeHandler.class),@Result(column = "api_create_user", property = "createUser"),@Result(column = "api_create_date", property = "createDate"),@Result(column = "api_update_user", property = "updateUser"),@Result(column = "api_update_date", property = "updateDate")})List<QMockApiEntity> getMockApiListByPath(String path, String method);}
特别指出Results标记中有一处 typeHandler = JSONObjectTypeHandler.class
是用到了MyBatis的自定义类型处理器,代码如下,具体技术点大家可以先自行学习下,后边有扩展内容的也会给大家说一说。
@MappedTypes(JSONObject.class)
@MappedJdbcTypes(JdbcType.VARCHAR)
public class JSONObjectTypeHandler extends BaseTypeHandler<JSONObject> {@Overridepublic void setNonNullParameter(PreparedStatement ps, int i, JSONObject parameter, JdbcType jdbcType) throws SQLException {ps.setString(i, String.valueOf(parameter.toJSONString()));}@Overridepublic JSONObject getNullableResult(ResultSet rs, String columnName) throws SQLException {String sqlJson = rs.getString(columnName);if (null != sqlJson){return JSONObject.parseObject(sqlJson);}return null;}@Overridepublic JSONObject getNullableResult(ResultSet rs, int columnIndex) throws SQLException {String sqlJson = rs.getString(columnIndex);if (null != sqlJson){return JSONObject.parseObject(sqlJson);}return null;}@Overridepublic JSONObject getNullableResult(CallableStatement cs, int columnIndex) throws SQLException {String sqlJson = cs.getString(columnIndex);if (null != sqlJson){return JSONObject.parseObject(sqlJson);}return null;}
}
业务逻辑
以上都准备好后,就可以调用逻辑了,注意这里之前说过因为网关服务没有太多的类和方法,所以没有写接口类,以及将所有service实现也都集成在一个类中了。
@Service("QMockService")
public class QMockService{@Autowiredprivate QMockApiMapper qMockApiMapper;public List<QMockApiEntity> selectApiByPath(String path, String method) {List<QMockApiEntity> mockApiEntities = qMockApiMapper.getMockApiListByPath(path, method);return mockApiEntities;}
}
截止到目前所有的JAVA代码文件的目录结构供大家参考如下:
最后一个步骤,对其之前的文章中的QMockInterceptor.java
进行升级改造,将拦截返回的逻辑处理部分变更调用API服务类,实现真正的配置查询和返回。
@Slf4j
public class QMockInterceptor implements HandlerInterceptor{@AutowiredQMockService qMockService;public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {// 此处省略重复代码... //JSONObject resResult = new JSONObject();Integer resCode = 200;JSONObject reqParamsOrBody = null;// 匹配查询URI的数据,当等于1的时候进行规则查询,否则其他错误请求处理List<QMockApiEntity> mockApiEntities = qMockService.findApiByPath(requestURI, requestMethod);if(mockApiEntities.size() == 1) {// 唯一匹配返回默认配置值resResult = mockApiEntities.get(0).getResDefault();resCode = mockApiEntities.get(0).getResCode();} else if (mockApiEntities.size() > 1) {resResult.put("code", 5000);resResult.put("data", new JSONObject());resResult.put("msg", "MOCK匹配多个URI请检查配置");}else {resResult.put("code", 5000);resResult.put("data", new JSONObject());resResult.put("msg", "MOCK未匹配任何URI请先添加把");}}
保存全部代码,启动网关服务做个PostMan请求验证下接口拦截功能是否一切OK
至此,本篇教程实现到这里,基本上我们已经实现一个mock网关服务的从请求-拦截-匹配-返回结果的这样的流程。在下一篇中我们去实现更深一层的规则匹配。