123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320 |
- package hikvisionOpenAPIGo
- import (
- "bytes"
- "crypto/hmac"
- "crypto/md5"
- "crypto/sha256"
- "crypto/tls"
- "encoding/base64"
- "encoding/hex"
- "encoding/json"
- "fmt"
- "github.com/gofrs/uuid"
- "io/ioutil"
- "net/http"
- "net/url"
- "sort"
- "strconv"
- "strings"
- "time"
- )
- // HKConfig 海康OpenAPI配置参数
- type HKConfig struct {
- Ip string //平台ip
- Port int //平台端口
- AppKey string //平台APPKey
- Secret string //平台APPSecret
- IsHttps bool //是否使用HTTPS协议
- }
- // 返回结果
- type Result struct {
- Code string `json:"code"`
- Msg string `json:"msg"`
- Data interface{} `json:"data"`
- }
- // 返回值data
- type Data struct {
- Total int `json:"total"`
- PageSize int `json:"pageSize"`
- PageNo int `json:"pageNo"`
- List []map[string]interface{} `json:"list"`
- }
- // @title HTTP Post请求
- // @url HTTP接口Url string HTTP接口Url,不带协议和端口
- // @body 请求参数 map[string]any 支持数组、结构体等
- // @return 请求结果 Result
- func (hk HKConfig) HttpPost(url string, body map[string]any, timeout int) (result Result, err error) {
- var header = make(map[string]string)
- bodyJson, err := json.Marshal(body)
- if err != nil {
- return result, err
- }
- err = hk.initRequest(header, url, string(bodyJson), true)
- if err != nil {
- return Result{}, err
- }
- var sb strings.Builder
- if hk.IsHttps {
- sb.WriteString("https://")
- } else {
- sb.WriteString("http://")
- }
- sb.WriteString(fmt.Sprintf("%s:%d%s", hk.Ip, hk.Port, url))
- client := &http.Client{
- Timeout: time.Duration(timeout) * time.Second,
- CheckRedirect: func(req *http.Request, via []*http.Request) error {
- return http.ErrUseLastResponse
- },
- }
- if hk.IsHttps {
- tr := &http.Transport{
- TLSClientConfig: &tls.Config{InsecureSkipVerify: true},
- }
- client.Transport = tr
- }
- req, err := http.NewRequest("POST", sb.String(), bytes.NewReader(bodyJson))
- if err != nil {
- return result, err
- }
- req.Header.Set("Accept", header["Accept"])
- req.Header.Set("Content-Type", header["Content-Type"])
- for k, v := range header {
- if strings.HasPrefix(k, "x-ca-") {
- req.Header.Set(k, v)
- }
- }
- resp, err := client.Do(req)
- if err != nil {
- return result, err
- }
- defer resp.Body.Close()
- if resp.StatusCode == http.StatusOK {
- resBody, _ := ioutil.ReadAll(resp.Body)
- err = json.Unmarshal(resBody, &result)
- return result, err
- } else if resp.StatusCode == http.StatusFound || resp.StatusCode == http.StatusMovedPermanently {
- reqUrl := resp.Header.Get("Location")
- err = fmt.Errorf("HttpPost Response StatusCode:%d,Location:%s", resp.StatusCode, reqUrl)
- } else {
- err = fmt.Errorf("HttpPost Response StatusCode:%d", resp.StatusCode)
- }
- return result, err
- }
- // @title HTTP Get请求
- // @url HTTP接口Url string HTTP接口Url,不带协议和端口
- // @params 请求参数 map[string]any 支持数组、结构体等
- // @timeout 超时时间 int 单位秒
- // @return 请求结果 Result
- func (hk HKConfig) HttpGet(url string, params map[string]any, timeout int) (result Result, err error) {
- var header = make(map[string]string)
- // 构造查询参数
- queryParams := buildQueryParams(params)
- u := fmt.Sprintf("%s:%d%s?%s", hk.Ip, hk.Port, url, queryParams)
- var sb strings.Builder
- if hk.IsHttps {
- sb.WriteString("https://")
- } else {
- sb.WriteString("http://")
- }
- sb.WriteString(u)
- client := &http.Client{
- Timeout: time.Duration(timeout) * time.Second,
- CheckRedirect: func(req *http.Request, via []*http.Request) error {
- return http.ErrUseLastResponse
- },
- }
- if hk.IsHttps {
- tr := &http.Transport{
- TLSClientConfig: &tls.Config{InsecureSkipVerify: true},
- }
- client.Transport = tr
- }
- req, err := http.NewRequest("GET", sb.String(), nil)
- if err != nil {
- return result, err
- }
- // 初始化签名头
- err = hk.initRequest(header, url, "", false)
- if err != nil {
- return result, err
- }
- req.Header.Set("Accept", header["Accept"])
- req.Header.Set("Content-Type", header["Content-Type"])
- for k, v := range header {
- if strings.HasPrefix(k, "x-ca-") {
- req.Header.Set(k, v)
- }
- }
- resp, err := client.Do(req)
- if err != nil {
- return result, err
- }
- defer resp.Body.Close()
- if resp.StatusCode == http.StatusOK {
- resBody, _ := ioutil.ReadAll(resp.Body)
- err = json.Unmarshal(resBody, &result)
- return result, err
- } else if resp.StatusCode == http.StatusFound || resp.StatusCode == http.StatusMovedPermanently {
- reqUrl := resp.Header.Get("Location")
- err = fmt.Errorf("HttpGet Response StatusCode:%d,Location:%s", resp.StatusCode, reqUrl)
- } else {
- err = fmt.Errorf("HttpGet Response StatusCode:%d", resp.StatusCode)
- }
- return result, err
- }
- // initRequest 初始化请求头
- func (hk HKConfig) initRequest(header map[string]string, url, body string, isPost bool) error {
- header["Accept"] = "application/json"
- header["Content-Type"] = "application/json"
- if isPost {
- var err error
- header["content-md5"], err = computeContentMd5(body)
- if err != nil {
- return err
- }
- }
- header["x-ca-timestamp"] = strconv.FormatInt(time.Now().UnixMilli(), 10)
- uid, err := uuid.NewV4()
- if err != nil {
- return err
- }
- header["x-ca-nonce"] = uid.String()
- header["x-ca-key"] = hk.AppKey
- var strToSign string
- if isPost {
- strToSign = buildSignString(header, url, "POST")
- } else {
- strToSign = buildSignString(header, url, "GET")
- }
- signedStr, err := computeForHMACSHA256(strToSign, hk.Secret)
- if err != nil {
- return err
- }
- header["x-ca-signature"] = signedStr
- return nil
- }
- // computeContentMd5 计算content-md5
- func computeContentMd5(body string) (string, error) {
- h := md5.New()
- _, err := h.Write([]byte(body))
- if err != nil {
- return "", err
- }
- md5Str := hex.EncodeToString(h.Sum(nil))
- return base64.StdEncoding.EncodeToString([]byte(md5Str)), nil
- }
- // computeForHMACSHA256 计算HMACSHA265
- func computeForHMACSHA256(str, secret string) (string, error) {
- mac := hmac.New(sha256.New, []byte(secret))
- _, err := mac.Write([]byte(str))
- if err != nil {
- return "", err
- }
- return base64.StdEncoding.EncodeToString(mac.Sum(nil)), nil
- }
- // buildSignString 计算签名字符串
- func buildSignString(header map[string]string, url, method string) string {
- var sb []string
- sb = append(sb, strings.ToUpper(method))
- sb = append(sb, "\n")
- if header != nil {
- if _, ok := header["Accept"]; ok {
- sb = append(sb, header["Accept"])
- sb = append(sb, "\n")
- }
- if _, ok := header["Content-MD5"]; ok {
- sb = append(sb, header["Content-MD5"])
- sb = append(sb, "\n")
- }
- if _, ok := header["Content-Type"]; ok {
- sb = append(sb, header["Content-Type"])
- sb = append(sb, "\n")
- }
- }
- sb = append(sb, buildSignHeader(header))
- sb = append(sb, url)
- return strings.Join(sb, "")
- }
- // buildSignHeader 计算签名头
- func buildSignHeader(header map[string]string) string {
- var sslice []string
- for key := range header {
- if strings.Contains(key, "x-ca-") {
- sslice = append(sslice, key)
- }
- }
- sort.Strings(sslice)
- var sbSignHeader []string
- var sb []string
- for _, k := range sslice {
- sb = append(sb, k+":")
- if header[k] != "" {
- sb = append(sb, header[k])
- }
- sb = append(sb, "\n")
- sbSignHeader = append(sbSignHeader, k)
- }
- header["x-ca-signature-headers"] = strings.Join(sbSignHeader, ",")
- return strings.Join(sb, "")
- }
- // buildQueryParams 构建查询参数字符串,支持数组、基本类型
- func buildQueryParams(params map[string]any) string {
- values := make(url.Values)
- for key, value := range params {
- switch v := value.(type) {
- case string:
- values.Add(key, v)
- case int:
- values.Add(key, strconv.Itoa(v))
- case bool:
- values.Add(key, strconv.FormatBool(v))
- case []string:
- for _, item := range v {
- values.Add(key+"[]", item)
- }
- case []int:
- for _, item := range v {
- values.Add(key+"[]", strconv.Itoa(item))
- }
- case []interface{}:
- for _, item := range v {
- values.Add(key+"[]", fmt.Sprintf("%v", item))
- }
- default:
- values.Add(key, fmt.Sprintf("%v", v))
- }
- }
- return values.Encode()
- }
|