前言
对接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文件中的数据我们可以在读取出来后存在本地(数据库或者放在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, }
使用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 }
使用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 }
处理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数据转换为结构体参考我之前的这篇文章: