上两节我们学习了 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 '接口管理表';
表创建完后我们再往里插入两条数据方便后边的接口开发调试。
Img
在之前的分享中已经讲过层级架构和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> {
@Override
public void setNonNullParameter(PreparedStatement ps, int i, JSONObject parameter, JdbcType jdbcType) throws SQLException {
ps.setString(i, String.valueOf(parameter.toJSONString()));
}
@Override
public JSONObject getNullableResult(ResultSet rs, String columnName) throws SQLException {
String sqlJson = rs.getString(columnName);
if (null != sqlJson){
return JSONObject.parseObject(sqlJson);
}
return null;
}
@Override
public JSONObject getNullableResult(ResultSet rs, int columnIndex) throws SQLException {
String sqlJson = rs.getString(columnIndex);
if (null != sqlJson){
return JSONObject.parseObject(sqlJson);
}
return null;
}
@Override
public 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{
@Autowired
private QMockApiMapper qMockApiMapper;
public List<QMockApiEntity> selectApiByPath(String path, String method) {
List<QMockApiEntity> mockApiEntities = qMockApiMapper.getMockApiListByPath(path, method);
return mockApiEntities;
}
}
截止到目前所有的JAVA代码文件的目录结构供大家参考如下:
Img
最后一个步骤,对其之前的文章中的QMockInterceptor.java
进行升级改造,将拦截返回的逻辑处理部分变更调用API服务类,实现真正的配置查询和返回。
@Slf4j
public class QMockInterceptor implements HandlerInterceptor{
@Autowired
QMockService 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
Img
至此,本篇教程实现到这里,基本上我们已经实现一个mock网关服务的从请求-拦截-匹配-返回结果的这样的流程。在下一篇中我们去实现更深一层的规则匹配。