接口测试 基于 http 代理的响应 mock 工具

JoyMao · 2021年11月01日 · 3039 次阅读

fiddler 一类的工具可以修改响应来做 mock 工具,支持 url 重写,本地文件替换响应和一些固定的状态码返回等,但想更新响应头或者进行请求,需要修改 fiddler Script。
所以这里萌生写 1 个 http(s) 专门来做这种代理型 mock 工具的想法。

http(s) 代理选用的是"github.com/elazarl/goproxy"这个包(其实这个包已经好久没更新了,且不支持 http/2(会降级),凑合着用吧)

主要修改点

proxy 开启中间人攻击:

proxy.OnRequest().HandleConnect(goproxy.AlwaysMitm)

生成自定义的证书

goproxyCa, err := tls.X509KeyPair(caCert, caKey)
    if err != nil {
        return err
    }
    // x509是Windows支持的cert格式
    if goproxyCa.Leaf, err = x509.ParseCertificate(goproxyCa.Certificate[0]); err != nil {
        return err
    }
    if goproxyCa.Leaf.NotAfter.Before(time.Now()){
        return errors.New("current cert is expired, need using openssl to make a new one")
    }
    filePath:="./z.x509.cer"
    if _,err:=os.Stat(filePath);err!=nil{
        if os.IsNotExist(err){ // 如果不存在,则创建新的证书
            certX509,err:=os.OpenFile(filePath,os.O_RDWR|os.O_TRUNC|os.O_CREATE, 0766)
            if err!=nil{
                fmt.Println(err)
            }
            defer certX509.Close()
            certX509.Write(goproxyCa.Leaf.Raw)
        }
    }
    goproxy.GoproxyCa = goproxyCa
    goproxy.OkConnect = &goproxy.ConnectAction{Action: goproxy.ConnectAccept, TLSConfig: goproxy.TLSConfigFromCA(&goproxyCa)}
    goproxy.MitmConnect = &goproxy.ConnectAction{Action: goproxy.ConnectMitm, TLSConfig: goproxy.TLSConfigFromCA(&goproxyCa)}
    goproxy.HTTPMitmConnect = &goproxy.ConnectAction{Action: goproxy.ConnectHTTPMitm, TLSConfig: goproxy.TLSConfigFromCA(&goproxyCa)}
    goproxy.RejectConnect = &goproxy.ConnectAction{Action: goproxy.ConnectReject, TLSConfig: goproxy.TLSConfigFromCA(&goproxyCa)}

更新响应的头及内容

func updateResponse(resp *http.Response, r *respRule) {
    resp.StatusCode = 200
    resp.Header.Del("Location")
    if r.RespAction.SetHeaders!=nil { // 设置请求头
        for _,sh:=range r.RespAction.SetHeaders{
            resp.Header.Set(sh.Header,sh.Value)
        }
    }
    if r.RespAction.SetBody!= (setBody{}) {
        switch r.RespAction.SetBody.BodyType {
        case 0:
            resp.Header.Set("Content-Type", ContentTypeJson)
        case 1:
            resp.Header.Set("Content-Type", ContentTypeText)
        case 2:
            resp.Header.Set("Content-Type", ContentTypeHtml)
        case 3:
            resp.Header.Set("Content-Type", ContentTypeJpeg)
        default:
            resp.Header.Set("Content-Type", ContentTypeText)
        }
        respFile,err:=os.Open(r.RespAction.SetBody.BodyFile)
        if err!=nil{
            log.Printf("文件%s无法打开\n",r.RespAction.SetBody.BodyFile)
            return
        }
        defer respFile.Close()
        rBytes,err:=ioutil.ReadAll(respFile)
        if err!=nil{
            log.Printf("文件%s打开内容报错请检查\n",r.RespAction.SetBody.BodyFile)
            return
        }
        buf:=bytes.NewBuffer(rBytes)
        resp.ContentLength = int64(buf.Len())
        resp.Body=ioutil.NopCloser(buf)
    }
}

使用说明

【一】添加证书为根路径信任证书

windows 安装 z.x509.cer 证书(双击,不小心删除也没关系会重新生成),选择第三方根证书颁发机构,这样重启浏览器,使用 go_httpmock 的代理时的 https 请求就可信任了。
另外直接访问 127.0.0.1:8088 页面直接提供下载证书

【二】配置上行(upstream)代理

在启动之前配置 upstreamProxyConfig.json 即可设置上行代理,结构如下:

upstreamProxyConfig.json
{
  "proxyActive": false,
  "proxyUrl":"http://192.168.16.67:8080",
  "proxyUser": "",
  "proxyPassword": ""
}
  • proxyActive: false 表示不使用上行代理,true 表示启用
  • proxyUrl:如上格式,表示使用我们的 P 版 67 代理,如想转发到 fiddler 上,可以配置"http://127.0.0.1:8888"
  • proxyUser,proxyPassword:则表示上行代理需要验证的情况,输入对应用户名、密码

【三】代理规则(rules.json)说明

规则文件 rules.json 可随意更改,每隔 10s 自动更新一次(控制台中有提示)
有 2 种响应 mock,1 种是新建 http 响应,另 1 种是修改 http 响应。

新建 http 响应

这种不要求服务端可用,使用构造响应或转发 url 来返回相应,对应字段"newRespRules",它是个规则的列表。

【1】构造响应的规则的结构

构造响应 折叠源码

{
      "active": true,
      "urlMatchRegexp": "/api/v1/account/getUserInfo",
      "respAction": {
        "setHeaders": [
          {"header": "Access-Control-Allow-Origin","value": "https://www.doba.com"},
          {"header": "Set-Cookie","value": "lui=VjZnM1N0eGlYQnNZVlNjeTJHWjI0UT09;path=/;domain=.doba.com;HttpOnly"}
        ],

         "bodyFile": "./respFiles/getUserInfo1.json"

      }
    }
  • active:false 表示规则禁用,true 表示规则启用
  • urlMatchRegexp:表示 url 匹配的正则表示式 (注意 json 中\要改成\)
  • respAction:包含 setHeaders 和 setBody
  • setBody:因为是是构造响应,所以 bodyFile 必须设置,填写对应响应文件的路径(比如正常情况都放在 respFiles 文件夹下)
  • setHeaders:是设置相应头的规则列表,比如这里设置了 2 个响应头 Access-Control-Allow-Origin 和设置 cookie。如不想设置,"setHeaders":[] 或"setHeaders":null
【2】转发 url 的规则的结构

转发 url 折叠源码

{
      "active": false,
      "urlMatchRegexp": "(channelhub/api/v1/shoppingCart/list)",
      "reWriteUrl": "http://www.baidu.com/${1}",
      "respAction":null
 }
  • active:false 表示规则禁用,true 表示规则启用
  • urlMatchRegexp:表示 url 匹配的正则表示式 (注意 json 中\要改成\),如用 (...) 表示这里可以获取子匹配,可以用于 reWriteUrl 中
  • reWriteUrl:转发的 url,可以使用 urlMatchRegexp 的子匹配 (${1}、${2}...),比如这里表示转发到"http://www.baidu.com/channelhub/api/v1/shoppingCart/list"
  • respAction:当 reWriteUrl 有内容时,respAction 就没有用了,这里设置 null 即可

修改 http 响应

这种要求服务端可用,使用规则来更新服务端返回的响应的头及内容,对应字段"updateRespRules",它是个规则的列表。
更新响应的规则的结构

{
      "active": true,
      "urlMatchRegexp": "/api/v1/account/getUserInfo",
      "respAction": {
        "setHeaders": [
          {"header": "Access-Control-Allow-Origin","value": "https://www.doba.com"},
          {"header": "Set-Cookie","value": "lui=VjZnM1N0eGlYQnNZVlNjeTJHWjI0UT09;path=/;domain=.doba.com;HttpOnly"}
        ],

         "bodyFile": "./respFiles/getUserInfo1.json"

      }
    }

与构造响应的规则结构与规则均一致,不多说了,但 setHeaders、setBody 都可为 null.

  • 和新建 http 响应不一样的地方是如果响应是 json,bodyFile 中的文件如果不存在,会自动将服务器端的响应 json 格式化后自动生成对应文件,可以直接进行对应修改了。

【四】响应文件(存放 respFiles 中)说明

即存放需要替换响应的文件,比如 json、图片、html 等。
比如,通过 chrome 获取的接口响应内容 json,复制并保存下来,在 rules 中配置好路径,就可以用了。
内容随便改,响应随便变。

目前已经生成了 exe 文件,在 release 中
具体代码见:https://github.com/mao303mao/go_httpmock

共收到 0 条回复 时间 点赞
JoyMao 关闭了讨论 11月22日 13:53
JoyMao 重新开启了讨论 12月09日 16:00
JoyMao 再次优化下利用 http 代理修改响应的小工具 中提及了此贴 06月13日 11:24
需要 登录 后方可回复, 如果你还没有账号请点击这里 注册