龙空技术网

【测试平台系列】第一章 手撸压力机(九)- 封装函数

被测试耽误的大厨 41

前言:

现在朋友们对“什么叫封装函数”大约比较看重,大家都需要剖析一些“什么叫封装函数”的相关知识。那么小编同时在网络上网罗了一些对于“什么叫封装函数””的相关资讯,希望朋友们能喜欢,咱们快快来学习一下吧!

到这里,大家已经知道如何通过一个web服务,来构建我们的压力机的逻辑了。上一篇我们已经通过/engin/run/testObject/接口完成了,我们对一次接口请求的调用。算是大家对这种设计思想有了初步的了解。但是我们只是完成了初步的逻辑处理,这一篇我们继续进行优化。

将我们的一些代码封装到函数和方法中,这样我们看来代码可读性更好。如果发现bug,也可以更好的进行追踪。

我们首先将header\query\body\cookie结构体进行一下变更。

// Header headertype Header struct {        IsEnable  bool   `json:"is_enable"`  // 是否启用        Field     string `json:"field"`      // 字段名称        Value     string `json:"value"`      // 字段值        FieldType string `json:"field_type"` // 字段类型}// Query querytype Query struct {        IsEnable  bool   `json:"is_enable"` // 是否启用        Field     string `json:"field"`        Value     string `json:"value"`        FieldType string `json:"field_type"`}// Cookie cookietype Cookie struct {        IsEnable  bool   `json:"is_enable"` // 是否启用        Field     string `json:"field"`        Value     string `json:"value"`        FieldType string `json:"field_type"`}// Body bodytype Body struct {        Type      string     `json:"type"`        Raw       string     `json:"raw"`        Parameter []*VarForm `json:"parameter"`}// VarForm 参数表type VarForm struct {         IsEnable    bool        `json:"is_enable" bson:"is_enable"`        Type        string      `json:"type" bson:"type"`        FileBase64  []string    `json:"fileBase64"`        Key         string      `json:"key" bson:"key"`        Value       interface{} `json:"value" bson:"value"`        NotNull     int64       `json:"not_null" bson:"not_null"`        Description string      `json:"description" bson:"description"`        FieldType   string      `json:"field_type" bson:"field_type"`}

在global目录下新建constant/constant.go存放我们使用的常量。

// Package constant -----------------------------// @file      : constant.go// @author    : 被测试耽误的大厨// @contact   : 13383088061@163.com// @time      : 2023/7/5 18:30// -------------------------------------------package constant// http请求body的类型, 由于http的body有很多类型,所以我们定义使用常量定义好这些类型const (        NoneMode      = "none"        FormMode      = "multipart/form-data"        UrlencodeMode = "application/x-www-form-urlencoded"        JsonMode      = "application/json"        XmlMode       = "application/xml"        JSMode        = "application/javascript"        PlainMode     = "text/plain"        HtmlMode      = "text/html")

然后在http_request.go文件如新增如下的HttpRequest对象的方法

// 设置urlfunc (hr *HttpRequest) urlInit(req *fasthttp.Request) {        // 去除url两端的空格        hr.Url = strings.TrimSpace(hr.Url)        // 判断url是否以http或者https开头,如果不是,则给url加上http://        if !strings.HasPrefix(hr.Url, "http") && !strings.HasPrefix(hr.Url, "https") {                hr.Url = fmt.Sprintf("%s%s", ";, hr.Url)        }        // 添加该请求的http的url        req.SetRequestURI(hr.Url)}// 设置methodfunc (hr *HttpRequest) methodInit(req *fasthttp.Request) {        // 将method转换为大写,如get->GET        hr.Method = strings.ToUpper(hr.Method)        // 添加该请求的http方法:get、post、delete、update等等        req.Header.SetMethod(hr.Method)}// 设置headerfunc (hr *HttpRequest) headerInit(req *fasthttp.Request) {        for _, header := range hr.Headers {                // 该跳header没有启用,则跳过                if !header.IsEnable {                        continue                }                // 去除header的key、value中两端的空格                header.Field = strings.TrimSpace(header.Field)                header.Value = strings.TrimSpace(header.Value)                if strings.EqualFold(header.Field, "host") {                        // 由于在header中设置host不生效,所以需要强制设置生效                        req.SetHost(header.Value)                        req.UseHostHeader = true                } else {                        req.Header.Add(header.Field, header.Value)                }        }}// 设置queryfunc (hr *HttpRequest) queryInit(req *fasthttp.Request) {        // 如果query不为空则设置query        urlQuery := req.URI().QueryArgs()        for _, query := range hr.Querys {                if !query.IsEnable {                        continue                }                // 去除query的key中两端空格                query.Field = strings.TrimSpace(query.Field)                // 如果url中已经包含了该query的key则跳过                if strings.Contains(hr.Url, query.Field+"=") {                        continue                }                // 去除query的value中两端空格                query.Value = strings.TrimSpace(query.Value)                queryBy := []byte(query.Value)                urlQuery.AddBytesV(query.Field, queryBy)                hr.Url += fmt.Sprintf("&%s=%s", query.Field, query.Value)        }}// 设置cookiefunc (hr *HttpRequest) cookieInit(req *fasthttp.Request) {        // 设置cookie        for _, cookie := range hr.Cookies {                if !cookie.IsEnable {                        continue                }                // 去除header的key、value中两端的空格                cookie.Field = strings.TrimSpace(cookie.Field)                cookie.Value = strings.TrimSpace(cookie.Value)                req.Header.SetCookie(cookie.Field, cookie.Value)        }}// 设置bodyfunc (hr *HttpRequest) bodyInit(req *fasthttp.Request) {        if hr.Body == nil {                return        }        switch hr.Body.Type {        case constant.JSMode, constant.JsonMode, constant.HtmlMode, constant.PlainMode, constant.XmlMode:                req.Header.SetContentType(hr.Body.Type)                req.SetBody([]byte(hr.Body.Raw))        }}

修改HttpRequest对象的Request方法如下:

func (hr *HttpRequest) Request(response *TestObjectResponse) {        // 使用fasthttp 协程池        // 新建一个http请求        req := fasthttp.AcquireRequest()        defer fasthttp.ReleaseRequest(req)        // 新建一个http响应接受服务端的返回        resp := fasthttp.AcquireResponse()        defer fasthttp.ReleaseResponse(resp)        // 新建一个http的客户端, newHttpClient是一个方法,在下面        client := newHttpClient(hr.HttpClientSettings)        // 设置请求方法        hr.methodInit(req)        // 设置header        hr.headerInit(req)        // 设置query        hr.queryInit(req)        // 设置cookie        hr.cookieInit(req)        // 设置url        hr.urlInit(req)        // 设置body        hr.bodyInit(req)        // 记录开始时间        startTime := time.Now()        // 开始请求        err := client.Do(req, resp)        // 计算响应时间差值        requestTime := time.Since(startTime)        response.RequestTime = requestTime.Milliseconds()        response.Code = resp.StatusCode()        if err != nil {                response.Response = err.Error()                return        }        response.Response = string(resp.Body())}

目前http_request.go文件全部代码如下:

package modelimport (        "crypto/tls"        "crypto/x509"        "fmt"        "github.com/valyala/fasthttp"        "io/ioutil"        "kitchen-engine/global/constant"        "strings"        "time")// HttpRequest http请求的结构type HttpRequest struct {        Url                string             `json:"url"`                  // 接口uri        Method             string             `json:"method"`               // 接口方法,Get Post Update...        Headers            []Header           `json:"headers"`              // 接口请求头        Querys             []Query            `json:"querys"`               // get请求时的url        Cookies            []Cookie           `json:"cookies"`              // cookie        Body               *Body              `json:"body"`                 // 请求提        HttpClientSettings HttpClientSettings `json:"http_client_settings"` // http客户端配置}// 设置urlfunc (hr *HttpRequest) urlInit(req *fasthttp.Request) {        // 去除url两端的空格        hr.Url = strings.TrimSpace(hr.Url)        // 判断url是否以http或者https开头,如果不是,则给url加上http://        if !strings.HasPrefix(hr.Url, "http") || !strings.HasPrefix(hr.Url, "https") {                hr.Url = fmt.Sprintf("%s%s", ";, hr.Url)        }        // 添加该请求的http的url        req.SetRequestURI(hr.Url)}// 设置methodfunc (hr *HttpRequest) methodInit(req *fasthttp.Request) {        // 将method转换为大写,如get->GET        hr.Method = strings.ToUpper(hr.Method)        // 添加该请求的http方法:get、post、delete、update等等        req.Header.SetMethod(hr.Method)}// 设置headerfunc (hr *HttpRequest) headerInit(req *fasthttp.Request) {        for _, header := range hr.Headers {                // 该跳header没有启用,则跳过                if !header.IsEnable {                        continue                }                // 去除header的key、value中两端的空格                header.Field = strings.TrimSpace(header.Field)                header.Value = strings.TrimSpace(header.Value)                if strings.EqualFold(header.Field, "host") {                        // 由于在header中设置host不生效,所以需要强制设置生效                        req.SetHost(header.Value)                        req.UseHostHeader = true                } else {                        req.Header.Add(header.Field, header.Value)                }        }}// 设置queryfunc (hr *HttpRequest) queryInit(req *fasthttp.Request) {        // 如果query不为空则设置query        urlQuery := req.URI().QueryArgs()        for _, query := range hr.Querys {                if !query.IsEnable {                        continue                }                // 去除query的key中两端空格                query.Field = strings.TrimSpace(query.Field)                // 如果url中已经包含了该query的key则跳过                if strings.Contains(hr.Url, query.Field+"=") {                        continue                }                // 去除query的value中两端空格                query.Value = strings.TrimSpace(query.Value)                queryBy := []byte(query.Value)                urlQuery.AddBytesV(query.Field, queryBy)                hr.Url += fmt.Sprintf("&%s=%s", query.Field, query.Value)        }}// 设置cookiefunc (hr *HttpRequest) cookieInit(req *fasthttp.Request) {        // 设置cookie        for _, cookie := range hr.Cookies {                if !cookie.IsEnable {                        continue                }                // 去除header的key、value中两端的空格                cookie.Field = strings.TrimSpace(cookie.Field)                cookie.Value = strings.TrimSpace(cookie.Value)                req.Header.SetCookie(cookie.Field, cookie.Value)        }}// 设置bodyfunc (hr *HttpRequest) bodyInit(req *fasthttp.Request) {        if hr.Body == nil {                return        }        switch hr.Body.Type {        case constant.JSMode, constant.JsonMode, constant.HtmlMode, constant.PlainMode, constant.XmlMode:                req.Header.SetContentType(hr.Body.Type)                req.SetBody([]byte(hr.Body.Raw))        }}func (hr *HttpRequest) Request(response *TestObjectResponse) {        // 使用fasthttp 协程池        // 新建一个http请求        req := fasthttp.AcquireRequest()        defer fasthttp.ReleaseRequest(req)        // 新建一个http响应接受服务端的返回        resp := fasthttp.AcquireResponse()        defer fasthttp.ReleaseResponse(resp)        // 新建一个http的客户端, newHttpClient是一个方法,在下面        client := newHttpClient(hr.HttpClientSettings)        // 设置请求方法        hr.methodInit(req)        // 设置header        hr.headerInit(req)        // 设置query        hr.queryInit(req)        // 设置cookie        hr.cookieInit(req)        // 设置url        hr.urlInit(req)        // 设置body        hr.bodyInit(req)        // 记录开始时间        startTime := time.Now()        // 开始请求        err := client.Do(req, resp)        // 计算响应时间差值        requestTime := time.Since(startTime)        response.RequestTime = requestTime.Milliseconds()        response.Code = resp.StatusCode()        if err != nil {                response.Response = err.Error()                return        }        response.Response = string(resp.Body())}// 新建一个http客户端func newHttpClient(httpClientSettings HttpClientSettings) (httpClient *fasthttp.Client) {        // tls验证,关闭验证        tr := &tls.Config{                InsecureSkipVerify: true,        }        // 新建指针类型的客户端        httpClient = &fasthttp.Client{}        if httpClientSettings.Name != "" {                httpClient.Name = httpClientSettings.Name        }        if httpClientSettings.NoDefaultUserAgentHeader == true {                httpClient.NoDefaultUserAgentHeader = true        }        // 如果最大连接数不为0,将设置此数        if httpClientSettings.MaxConnsPerHost != 0 {                httpClient.MaxConnsPerHost = httpClientSettings.MaxConnsPerHost        }        // url不按照标准输出,按照原样输出        if httpClientSettings.DisablePathNormalizing == true {                httpClient.DisablePathNormalizing = true        }        // 请求头不按标准格式传输        if httpClientSettings.DisableHeaderNamesNormalizing == true {                httpClient.DisableHeaderNamesNormalizing = true        }        // 如果此时间不为0,那么将设置此时间。keep-alive维持此时长后将关闭。时间单位为毫秒        if httpClientSettings.MaxConnDuration != 0 {                httpClient.MaxConnDuration = time.Duration(httpClientSettings.MaxConnDuration) * time.Millisecond        }        if httpClientSettings.ReadTimeout != 0 {                httpClient.ReadTimeout = time.Duration(httpClientSettings.ReadTimeout) * time.Millisecond        }        if httpClientSettings.WriteTimeout != 0 {                httpClient.WriteTimeout = time.Duration(httpClientSettings.WriteTimeout) * time.Millisecond        }        // 该连接如果空闲的话,在此时间后断开。        if httpClientSettings.MaxIdleConnDuration != 0 {                httpClient.MaxIdleConnDuration = time.Duration(httpClientSettings.MaxIdleConnDuration) * time.Millisecond        }        //        httpsTls := httpClientSettings.AdvancedOptions.Tls        // 如果开启认证        if httpsTls.IsVerify {                // switch条件选择语句,如果认证类型为0:则表示双向认证,如果是1:则表示为单向认证                switch httpsTls.VerifyType {                case 0: // 开启双向验证                        tr.InsecureSkipVerify = false                        // 如果密钥文件为空则跳出switch语句                        if httpsTls.CaCert == "" {                                break                        }                        // 生成一个cert对象池                        caCertPool := x509.NewCertPool()                        if caCertPool == nil {                                fmt.Println("生成CertPool失败!")                                break                        }                        // 读取认证文件,读出后为字节                        key, err := ioutil.ReadFile(httpsTls.CaCert)                        // 如果读取错误,则跳出switch语句                        if err != nil {                                fmt.Println("打开密钥文件失败: ", err.Error())                                break                        }                        // 将认证文件添加到cert池中                        ok := caCertPool.AppendCertsFromPEM(key)                        // 如果添加失败则跳出switch语句                        if !ok {                                fmt.Println("密钥文件错误,生成失败!!!")                                break                        }                        // 将认证信息添加到客户端认证结构体                        tr.ClientCAs = caCertPool                case 1: // 开启单向验证,客户端验证服务端密钥                        tr.InsecureSkipVerify = false                }        }        fmt.Println("tr:    ", tr.InsecureSkipVerify)        // 客户端认证配置项        httpClient.TLSConfig = tr        return}// Header headertype Header struct {        IsEnable  bool   `json:"is_enable"`  // 是否启用        Field     string `json:"field"`      // 字段名称        Value     string `json:"value"`      // 字段值        FieldType string `json:"field_type"` // 字段类型}// Query querytype Query struct {        IsEnable  bool   `json:"is_enable"` // 是否启用        Field     string `json:"field"`        Value     string `json:"value"`        FieldType string `json:"field_type"`}// Cookie cookietype Cookie struct {        IsEnable  bool   `json:"is_enable"` // 是否启用        Field     string `json:"field"`        Value     string `json:"value"`        FieldType string `json:"field_type"`}// Body bodytype Body struct {        Type      string     `json:"type"`        Raw       string     `json:"raw"`        Parameter []*VarForm `json:"parameter"`}// VarForm 参数表type VarForm struct {        IsEnable    bool        `json:"is_enable" bson:"is_enable"`        Type        string      `json:"type" bson:"type"`        FileBase64  []string    `json:"fileBase64"`        Key         string      `json:"key" bson:"key"`        Value       interface{} `json:"value" bson:"value"`        NotNull     int64       `json:"not_null" bson:"not_null"`        Description string      `json:"description" bson:"description"`        FieldType   string      `json:"field_type" bson:"field_type"`}type HttpClientSettings struct {        //  客户端的名称,在header中的user-agent使用,通常我们默认就好        Name string `json:"name"`        // 默认为flase,表示User-Agent使用fasthttp的默认值        NoDefaultUserAgentHeader bool `json:"no_default_user_agent_header"`        // 每台主机可以建立的最大连接数。如果没有设置,则使用DefaultMaxConnsPerHost。        MaxConnsPerHost int `json:"max_conns_per_host"`        // 空闲的保持连接在此持续时间之后关闭。默认情况下,在DefaultMaxIdleConnDuration之后关闭空闲连接。        // 该连接如果空闲的话,在此时间后断开。        MaxIdleConnDuration int64 `json:"max_idle_conn_duration"`        // Keep-alive连接在此持续时间后关闭。默认情况下,连接时间是不限制的。        MaxConnDuration int `json:"max_conn_duration"`        // 默认情况下,响应读取超时时间是不限制的。        ReadTimeout int64 `json:"read_timeout"`        // 默认情况下,请求写超时时间不受限制。        WriteTimeout int64 `json:"write_timeout"`        // 请求头是否按标准格式传输        DisableHeaderNamesNormalizing bool `json:"disable_header_names_normalizing"`        // url路径是按照原样输出,还是按照规范化输出。默认按照规范化输出        DisablePathNormalizing bool            `json:"disable_path_normalizing"`        AdvancedOptions        AdvancedOptions `json:"advanced_options"` // 高级选项}// AdvancedOptions 高级选项type AdvancedOptions struct {        Tls Tls `json:"tls"` // 验证设置}// Tls tls认证结构体type Tls struct {        IsVerify   bool   `json:"is_verify"`   // 是否开启验证,默认不开启,开启后需要上传密钥文件        VerifyType int32  `json:"verify_type"` // 认证类型:0表示双向认证;1表示单向认证;默认为0        CaCert     string `json:"ca_cert"`     // 密钥文件}

好了,现在我们简单对http请求做了一些优化,下一步,我们开始将单个接口组成按业务流程组成场景进行使用。

标签: #什么叫封装函数