使用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 :\n %s \n Key:\n %s \n ", 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並存入數據庫的操作

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM