• 使用Go处理带证书的请求(含发送POST请求的具体实现)


    前言

    对接Apple渠道的API接口需要在请求中加上证书认证,源证书的格式为.p12,需要转成.pem格式存储Cert与PrivateKey信息,然后利用.pem文件中的信息发送请求。

    将p12文件转换为pem文件

    直接在mac终端使用命令即可完成:

    openssl pkcs12 -in xxx.p12 -out xxx.pem -nodes -clcerts

    获取pem文件数据并发送请求的示例 ***

    package main
    
    import (
        "crypto/tls"
        //"crypto/x509"
        "encoding/pem"
        "fmt"
        "io/ioutil"
        "net/http"
        //"testing"
    )
    
    // func TestPemRequest(t *testing.T) {
    func PemRequest() {
        b, _ := ioutil.ReadFile("/Users/Wanghongwei/Downloads/xxx.pem")
        pem.Decode(b)
        var pemBlocks []*pem.Block
        var v *pem.Block
        var pkey []byte
        for {
            v, b = pem.Decode(b)
            if v == nil {
                break
            }
            if v.Type == "PRIVATE KEY" {
                pkey = pem.EncodeToMemory(v)
            } else {
                pemBlocks = append(pemBlocks, v)
            }
        }
    
        bytes := pem.EncodeToMemory(pemBlocks[0])
        keyString  := string(pkey)
        CertString := string(bytes)
        fmt.Printf("Cert :
     %s 
     Key:
     %s 
     ", CertString, keyString)
        //pool := x509.NewCertPool()
        c, _ := tls.X509KeyPair(bytes, pkey)
        //pool.AppendCertsFromPEM(b)
    
        cfg := &tls.Config{
            Certificates:[]tls.Certificate{c},
        }
        tr := &http.Transport{
            TLSClientConfig: cfg,
        }
        client := &http.Client{Transport: tr}
        //
        request, _ := http.NewRequest("GET", "https://api.searchads.apple.com/api/v3/acls", nil)
        request.Header.Set("Content-Type", "application/json")
    
        resp, err := client.Do(request)
        // require.Nil(t, err)
        if err != nil{
            fmt.Println("err>>> ",err)
        }else{
            data, _ := ioutil.ReadAll(resp.Body)
            fmt.Printf(string(data))
        }
    }
    func main() {
        PemRequest()
    
    }
    获取pem文件的数据并发送请求的示例

    实践经验

    将pem文件中的信息存入配置项

    在实际使用中,账号的认证证书一般是不会改的,所以pem文件中的数据我们可以在读取出来后存在本地(数据库或者放在map中),程序用到的时候直接拿对应的参数即可,这里我是直接做成了配置文件:

    var NoPassMap = map[string]string{
        "certString": "xxx",
        "keyString":  "xxx",
        "orgId":      "x",
    }
    var RNoPassMap = map[string]string{
        "certString": "xxx",
        "keyString":  "xxx",
        "orgId":      "x",
    }
    var AppStareMap = map[string]string{
        "certString": "xxx",
        "keyString":  "zzz",
        "orgId":      "17zz",
    }
    
    // 账号字典
    var pemMap = map[string]map[string]string{
        "Nopass":    NoPassMap,
        "RNoPass":  RNoPassMap,
        "Appstare": AppStareMap,
    }
    pem数据的配置map

    使用Go发送POST请求

    与Get请求不一样,POST请求的数据是放在请求体中的,我们需要将所需要的请求数据构建成想要的格式,然后转成bytes.Reader格式的数据才能发送请求。

    请求体中的样例数据格式如下:

    POST https://api.searchads.apple.com/api/v3/reports/campaigns
    {
        "startTime": "2020-08-04",
        "endTime": "2020-08-14",
        "selector": {
            "orderBy": [
                {
                    "field": "countryOrRegion",
                    "sortOrder": "ASCENDING"
                }
            ],
            "conditions": [
                {
                    "field": "countriesOrRegions",
                    "operator": "CONTAINS_ANY",
                    "values": [
                        "US",
                        "GB"
                    ]
                },
                {
                    "field": "countryOrRegion",
                    "operator": "IN",
                    "values": [
                        "US"
                    ]
                }
            ],
            "pagination": {
                "offset": 0,
                "limit": 1000
            }
        },
        "groupBy": [
            "countryOrRegion"
        ],
        "timeZone": "UTC",
        "returnRecordsWithNoMetrics": true,
        "returnRowTotals": true,
        "returnGrandTotals": true
    }
    POST请求的样例格式

    使用go进行构建与转换的代码如下:

    func (a *appleTask) getPostBody(startDate, endDate string) (*bytes.Reader, *model.AppError) {
        // POST请求的请求体构建
        info := make(map[string]interface{})
        // 1、构建 orderBy 的筛选
        orderDic := map[string]string{
            "field":     "localSpend",
            "sortOrder": "ASCENDING",
        }
        orderLst := []interface{}{orderDic}
        // 2、构建 conditions 的筛选
        values1 := []interface{}{"false", "true"}
        conditionDic1 := map[string]interface{}{
            "field":    "deleted",
            "operator": "IN",
            "values":   values1,
        }
        conditionLst := []interface{}{
            conditionDic1,
        }
        // 3、构建分页的筛选
        paginationMap := map[string]interface{}{
            "offset": 0,
            "limit":  1000,
        }
        // 4、selector的数据
        SelectorObj := map[string]interface{}{
            "pagination": paginationMap,
            "orderBy":    orderLst,
            "conditions": conditionLst,
        }
        // selector
        info["selector"] = SelectorObj
        info["startTime"] = startDate
        info["endTime"] = endDate
        info["timeZone"] = "UTC"
        // info["returnRecordsWithNoMetrics"] = true
        //info["returnRowTotals"] = true
        //info["returnGrandTotals"] = true
        info["granularity"] = "DAILY"
        bytesData, err := json.Marshal(info)
        if err != nil {
            return nil, model.NewAppError("getPostBody", "Marshal.error", err.Error(), nil)
        }
        reader := bytes.NewReader(bytesData)
        return reader, nil
    }
    使用Go构建POST请求体中的参数

    处理HTTP请求并将结果转为string类型的json数据的代码如下:

    // base
    func (a *App) AppleBaseRequestString(pemObj *model.PemMsg, requestMethod, url string, requestBody *bytes.Reader) (string, *model.AppError) {
        // 注意得将上面得到的cert与privateKey这两个string类型的数据转换为bytes类型!
    certBytes :
    = []byte(pemObj.CertString) ketBytes := []byte(pemObj.KeyString) // 处理 c, _ := tls.X509KeyPair(certBytes, ketBytes) cfg := &tls.Config{ Certificates: []tls.Certificate{c}, } tr := &http.Transport{ TLSClientConfig: cfg, } client := &http.Client{Transport: tr} var request *http.Request var err error // get请求传nil,post请求传如上面代码中构建好的请求体数据 if requestBody == nil { request, err = http.NewRequest(requestMethod, url, nil) } else { request, err = http.NewRequest(requestMethod, url, requestBody) } if err != nil { return "", model.NewAppError("appleBaseRequestString", err.Error(), url, nil) } // 设置请求头 —— Apple的Ad专属认证的一个请求头 request.Header.Set("Authorization", fmt.Sprintf("orgId=%s", pemObj.OrgId)) // 注意POST请求必须加这个请求头 request.Header.Set("Content-Type", "application/json") resp, err1 := client.Do(request) if err1 != nil { return "", model.NewAppError("appleBaseRequestString", err1.Error(), url, nil) } data, err2 := ioutil.ReadAll(resp.Body) if err2 != nil { return "", model.NewAppError("appleBaseRequestString", err2.Error(), url, nil) } return string(data), nil }

    最后将得到的json数据转换为结构体参考我之前的这篇文章:

    使用Go解析HTTP返回数据为struct并存入数据库的操作

  • 相关阅读:
    LINQ To SQL: Eager Loading
    返回JSon格式数据
    Tips
    Easyui的DateBox日期格式化
    jquery treeview 展开指定节点,选中指定节点
    jquery treeview 功能参数
    Javascript 中 ShowModalDialog 的使用方法
    GetDlgItem用法
    20个开源项目托管站点推荐
    DLINQ(十): 分层构架的例子
  • 原文地址:https://www.cnblogs.com/paulwhw/p/14015824.html
Copyright © 2020-2023  润新知