接上一篇最外层接口的匹配,我们继续深入开发内层规则的匹配。其中规则的匹配会涉及到较多的判断处理,以及重复重复内容,所以我们先创建个匹配工具类,来进行代码类化开发。

public class QMockRuleUtil {
    // 规则匹配工具类
}

GET参数处理

实战项目为了简化处理,不论是GET参数还是POST BODY,不使用自带如之前讲到的getParameterMap方法,而是自定义一个方法,专门处理URL中请求参数"a=a1&b=b1&c=c1"成为JSON。如下代码处理进行一次&拆分,再进行一次=拆分。在这里值得注意的是对应值可能没有或者有多个等号的,我做了一定的兼容,但在实际应用过程中还可能出现其他特殊值的兼容情况,请想想如果遇到要如何处理?

public static JSONObject getJsonObjcetByQueryUrl(String paramStr){
        //String paramStr = "a=a1&b=b1&c=c1";
        String[] params = paramStr.split("&");
        JSONObject obj = new JSONObject();
        for (int i = 0; i < params.length; i++) {
            String[] param = params[i].split("=");
            if (param.length >= 2) {
                String key = param[0];
                String value = param[1];
                for (int j = 2; j < param.length; j++) {
                    value += "=" + param[j];
                }
                try {
                    obj.put(key,value);
                } catch (JSONException e) {
                    e.printStackTrace();
                }
            }
        }
        return obj;
    }

QMockInterceptor.java类中preHandle在匹配接口后,如果是GET请求,我们对其URL参数后的串获取并用自定义的处理工具类方法转换成统一的JSON数据。

if ("GET".equals(requestMethod.toUpperCase())){
    try {

        if (request.getParameterMap().size()!=0) {
            // 获取GET请求参数
            String paramsStr = URLDecoder.decode(request.getQueryString(),"utf-8");
            // 通过自定义转换方法转换成通用的json数据
            reqParamsOrBody = QMockRuleUtil.getJsonObjcetByQueryUrl(paramsStr);
        }
        // resResult = QMockRuleUtil.matchFilter(mockApiEntity,mockApiRuleEntities,reqParamsOrBody);
    }
    catch (JSONException e){
        log.error(e.toString());
    }
}

这样先完成了GET参数的处理,注意这里有一行注释,将在最后一个核心公共方法实现后打开。

POST参数处理

GET的请求参数处理相对简单些,而对于POST一般对应的Body参数,当然也会可以同传Params参数。之前就讲过本套案例不做太复杂逻辑处理,如果有需要请在本系完成对应实战操作后,掌握了对应的技术能力后,根据项目需求进行扩展开发,如果真是对于Mock需求过滤有复杂的要求,一般就需要在产品需求交互上定义清楚,然后代码层面分层处理。

// 获取POST方法的中的Body数据
public static String getBodyString(HttpServletRequest request) throws IOException {
    StringBuilder sb = new StringBuilder();
    InputStream inputStream = null;
    BufferedReader reader = null;
    try {
        inputStream = request.getInputStream();
        reader = new BufferedReader(new InputStreamReader(inputStream, Charset.forName("UTF-8")));
        String line = "";
        // 逐行读取
        while ((line = reader.readLine()) != null) {
            sb.append(line);
        }
    } catch (IOException e) {
        e.printStackTrace();
    } finally {
        if (inputStream != null) {
            try {
                inputStream.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        if (reader != null) {
            try {
                reader.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
    // 返回JSON字符串
    return sb.toString();
}

getBodyString 工具类方法字符流的处理方法是在一个POST请求的BODY本身就是要求JSON格式的前提下实现的逻辑。在得到这个JSON字符串后直接进行类型转换即可。其他非JSON格式请求很少见,如果需要请进行通过header传类型做响应匹配处理。

else if ("POST".equals(requestMethod.toUpperCase())){
    try {
        String strbody = QMockRuleUtil.getBodyString(request);
        if (!strbody.isEmpty()) {
            reqParamsOrBody = JSON.parseObject(strbody);
        }

        // resResult = QMockRuleUtil.matchFilter(mockApiEntity,mockApiRuleEntities,reqParamsOrBody);
    }
        catch (JSONException e){
        log.error(e.toString());
    }
} 

在最后我们进行获取规则,增加最后else处理,便可完成完整的逻辑匹配接口和规则的逻辑。

if(mockApiEntities.size() == 1) {
    // 取得 Mock api 唯一值
    QMockApiEntity mockApiEntity = mockApiEntities.get(0);

    // 根据 api id 查询规则列表
    List<QMockApiRuleEntity> mockApiRuleEntities = qMockService.selectApiRuleList(mockApiEntity.getId());
    log.info("Mock规则个数:" + mockApiRuleEntities.size());

    // 根据不同的方法做不同的处理,目前只支持常用的GET和POST
    if ("GET".equals(requestMethod.toUpperCase())){
        // GET参数处理部分
        resResult = QMockRuleUtil.matchFilter(mockApiEntity,mockApiRuleEntities,reqParamsOrBody);
    } else if ("POST".equals(requestMethod.toUpperCase())){
       // POST参数处理部分省略
       resResult = QMockRuleUtil.matchFilter(mockApiEntity,mockApiRuleEntities,reqParamsOrBody);
    } else {
        resResult.put("code", 4008);
        resResult.put("msg", "Mock暂未支持的请求方法");
    }

} 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请先添加把");
}

上述代码中有“根据 api id 查询规则列表” 的查询,这个查库实现参考上一篇和最初几篇套路式自己我先实现,也当做是检验下自我脱稿编程的代码能力的检验了。学习是需要自我实战才能有效果的,多花点时间将学过的东西独立实践,而不是照着练一遍,你将收获更大的进步。当然如果不会也不用担心,我将在下篇给出代码详细。

简单规则匹配

通过上述的GET/POST的参数获取转换最终得到一个reqParamsOrBody,然后还有上一篇中的得到mockApiEntitymockApiEntity 三个对象数据,这样我们就可以对其简单的匹配逻辑处理了,关键的解释放在代码注释里了。

 // Mock Rule 匹配逻辑
public static JSONObject matchFilter(QMockApiEntity mockApiEntity, List<QMockApiRuleEntity> mockApiRuleEntities, JSONObject reqParamsOrBody){

    JSONObject resResult = new JSONObject();

    // 循环规则列表
    for(QMockApiRuleEntity ruleEntity:mockApiRuleEntities){
        // 如果规则没有配置个过滤条件跳过
        if((ruleEntity.getReqFilter().isEmpty() || ruleEntity.getReqFilter() == null ) && reqParamsOrBody == null) {
            resResult = ruleEntity.getResBody();
            break;
        }

        // 如果有请求参数和配置条件都有进行匹配
        if(!ruleEntity.getReqFilter().isEmpty() && reqParamsOrBody != null) {

            // 规则大于请求参数认为不合理,不做此规则判定
            if (ruleEntity.getReqFilter().size() > reqParamsOrBody.size()){
                continue;
            }

            // 非合理条件均不存在进行key value的一一判定,这里同样简化只做一级过滤
            JSONArray filters = ruleEntity.getReqFilter();
            Boolean assertResult = false;
            for(int i=0; i< filters.size(); i++) {
                // 先看是否有匹配key+相等value 
                // 在页面添加规则的时候录入数据的固定格式为 [{"key":"job","value":"test"}]
                String key = filters.getJSONObject(i).getString("key");
                if(reqParamsOrBody.containsKey(key) && reqParamsOrBody.get(key).toString().equals(filters.getJSONObject(i).get("value"))) {
                    assertResult = true;
                } else {
                    assertResult = false;
                }
            }
            // 如果有获取规则返回值
            if( assertResult ) {
                resResult = ruleEntity.getResBody();
                break;
            }
        }
    }
    // 全没有返回默认
    if (resResult.isEmpty()) {
        resResult = mockApiEntity.getResDefault();
    }

    return resResult;
}

我们最终做下联调运行测试:

  • GET接口规则命中
    测试开发【Mock平台】13基础:拦截器服务实现(四) 简单规则匹配逻辑-LMLPHP

  • POST接口规则命中
    测试开发【Mock平台】13基础:拦截器服务实现(四) 简单规则匹配逻辑-LMLPHP

至此,本篇教程实现到这里,在上一篇基础上我们进一步实现了接口规则的匹配。有了这个能力支持,我们就可以针对同一个接口不同数值的请求匹配返回对应的值了。

下一篇我们会给出接口规则的请求表和全过程,下下篇是一个扩展知识点,然后我们回归到页面的交互开发。

02-20 03:44