需要一些关于如何基于Golang实现一个RESTful API应用的帮助。

huangapple go评论100阅读模式
英文:

Need some help on how to implement an restfull api app based on golang

问题

我的编码能力有点低 需要一些关于如何基于Golang实现一个RESTful API应用的帮助。
最近我开始学习golang和如何处理API通信应用程序。通过自学,我一直在享受学习的过程,golang是一种具有挑战性的语言,最终会带来巨大的回报(代码感 ^^)。

我一直在尝试为基于Cryptsy API V2(BETA)的golang创建一个API库,这是一个RESTful API。他们在他们的API网站上有一个Python库https://github.com/ScriptProdigy/CryptsyPythonV2/blob/master/Cryptsy.py。

到目前为止,我已经成功实现了公共访问,但是在私有访问方面遇到了很大的困难,因为认证部分让我感到非常困惑:(

> 授权是通过将以下变量发送到请求头密钥中来执行的
>
> - 公共API密钥。
> - 所有查询数据(nonce=blahblah&limit=blahblah)根据HMAC-SHA512方法由秘密密钥签名。您的秘密密钥和公共密钥可以从您的帐户设置页面生成。每个请求都需要一个唯一的nonce。(建议使用带有微秒的Unix时间戳)

Python代码如下所示:

def _query(self, method, id=None, action=None, query=[], get_method="GET"):
    query.append(('nonce', time.time()))
    queryStr = urllib.urlencode(query)
    
    link = 'https://' + self.domain + route
    
    sign = hmac.new(self.PrivateKey.encode('utf-8'), queryStr, hashlib.sha512).hexdigest()
            
    headers = {'Sign': sign, 'Key': self.PublicKey.encode('utf-8')}

在golang中,我已经做到了这一点:

package main

import(

    "crypto/hmac"
    "crypto/sha512"
    "encoding/hex"
    "encoding/json"
    "errors"
    "fmt"
    "io/ioutil"
    "net/http"
    "strings"
    "time"

)

const (

    API_BASE_CRY    = "https://api.cryptsy.com/api/"
    API_VERSION_CRY = "v2"
    API_KEY_CRY     = "xxxxx"
    API_SECRET_CRY  = "xxxxxxxxxxxx"
    DEFAULT_HTTPCLIENT_TIMEOUT = 30 // HTTP client timeout
)

type clientCry struct {
    apiKey     string
    apiSecret  string
    httpClient *http.Client
}

type Cryptsy struct {
    clientCry *clientCry
}

type CryptsyApiRsp struct {
    Success bool            `json:"success"`
    Data    json.RawMessage `json:"data"`
}

func NewCry(apiKey, apiSecret string) *Cryptsy {
    clientCry := NewClientCry(apiKey, apiSecret)
    return &Cryptsy{clientCry}
}

func NewClientCry(apiKey, apiSecret string) (c *clientCry) {
    return &clientCry{apiKey, apiSecret, &http.Client{}}
}

func ComputeHmac512Hex(secret, payload string) string {
    h := hmac.New(sha512.New, []byte(secret))
    h.Write([]byte(payload))
    return hex.EncodeToString(h.Sum(nil))
}

func (c *clientCry) doTimeoutRequestCry(timer *time.Timer, req *http.Request) (*http.Response, error) {

    type data struct {
	   resp *http.Response
	   err  error
    }

    done := make(chan data, 1)
    go func() {
	   resp, err := c.httpClient.Do(req)
	   done <- data{resp, err}
    }()

    select {
       case r := <-done:
	   return r.resp, r.err
       case <-timer.C:
	      return nil, errors.New("timeout on reading data from Bittrex API")
    }
}

func (c *clientCry) doCry(method string, ressource string, payload string, authNeeded bool) (response []byte, err error) {
    connectTimer := time.NewTimer(DEFAULT_HTTPCLIENT_TIMEOUT * time.Second)

    var rawurl string        

    nonce := time.Now().UnixNano()
    result :=  fmt.Sprintf("nonce=%d", nonce)
    rawurl = fmt.Sprintf("%s%s/%s?%s", API_BASE_CRY ,API_VERSION_CRY , ressource, result )
    req, err := http.NewRequest(method, rawurl, strings.NewReader(payload))

    sig := ComputeHmac512Hex(API_SECRET_CRY, result)
    
    req.Header.Add("Sign", sig)
    req.Header.Add("Key", API_KEY_CRY )
    
    resp, err := c.doTimeoutRequestCry(connectTimer, req)
    defer resp.Body.Close()
    
    response, err = ioutil.ReadAll(resp.Body)
    fmt.Println(fmt.Sprintf("reponse %s", response), err)
    return response, err
}

func main() {

    crypsy := NewCry(API_KEY_CRY, API_SECRET_CRY)
    r, _ := crypsy.clientCry.doCry("GET", "info", "", true) 
    fmt.Println(r)
}

我的输出是:

response {"success":false,"error":["Must be authenticated"]} <nil>

不知道为什么:(我在请求头中传递了公钥和签名,签名...我认为我在hmac-sha512中做得没错。
我正在查询用户信息URL https://www.cryptsy.com/pages/apiv2/user,根据API网站的说明,它没有任何额外的查询变量,所以只需要nonce..

我已经搜索了有关RESTful API的信息,但没有找到答案:(开始让我夜不能寐,因为我认为我所做的事情是正确的...真的找不到错误在哪里..

有没有人可以帮助我解决这个问题?

非常感谢 需要一些关于如何基于Golang实现一个RESTful API应用的帮助。

英文:

My coding skills are a bit low 需要一些关于如何基于Golang实现一个RESTful API应用的帮助。
Recently i started learning golang and how to handle an Api communication app. Have been having a great time learning it by myself, golang is revealing itself as a challenging language with great rewards in the end (code sense ^^).

Have been trying to create a cryptsy api lib for golang based on their API V2 (BETA) which is a restfull api. They have a python lib on their api website https://github.com/ScriptProdigy/CryptsyPythonV2/blob/master/Cryptsy.py.

So far have been able to get the public access working but am having a really hard time at the private access because of the authentication part.. I find that the info they give on their website on how to implement it is a bit confusing 需要一些关于如何基于Golang实现一个RESTful API应用的帮助。

> Authorization is performed by sending the following variables into the request header Key
>
> - Public API key.
> - All query data (nonce=blahblah&limit=blahblah) signed by a secret key according to HMAC-SHA512 method. Your secret key and public keys can be generated from your account settings page. Every request requires a unique nonce. (Suggested to use unix timestamp with microseconds)

For this authentication part the python code goes as:

def _query(self, method, id=None, action=None, query=[], get_method="GET"):
    query.append(('nonce', time.time()))
    queryStr = urllib.urlencode(query)
    
    link = 'https://' + self.domain + route
    
    sign = hmac.new(self.PrivateKey.encode('utf-8'), queryStr, hashlib.sha512).hexdigest()
            
    headers = {'Sign': sign, 'Key': self.PublicKey.encode('utf-8')}

Got this far in golang:

package main

import(

    "crypto/hmac"
    "crypto/sha512"
    "encoding/hex"
    "encoding/json"
    "errors"
    "fmt"
    "io/ioutil"
    "net/http"
    "strings"
    "time"

)

const (

    API_BASE_CRY    = "https://api.cryptsy.com/api/"
    API_VERSION_CRY = "v2"
    API_KEY_CRY     = "xxxxx"
    API_SECRET_CRY  = "xxxxxxxxxxxx"
    DEFAULT_HTTPCLIENT_TIMEOUT = 30 // HTTP client timeout
)

type clientCry struct {
    apiKey     string
    apiSecret  string
    httpClient *http.Client
}

type Cryptsy struct {
    clientCry *clientCry
}

type CryptsyApiRsp struct {
    Success bool            `json:"success"`
    Data    json.RawMessage `json:"data"`
}

func NewCry(apiKey, apiSecret string) *Cryptsy {
    clientCry := NewClientCry(apiKey, apiSecret)
    return &Cryptsy{clientCry}
}

func NewClientCry(apiKey, apiSecret string) (c *clientCry) {
    return &clientCry{apiKey, apiSecret, &http.Client{}}
}

func ComputeHmac512Hex(secret, payload string) string {
    h := hmac.New(sha512.New, []byte(secret))
    h.Write([]byte(payload))
    return hex.EncodeToString(h.Sum(nil))
}

func (c *clientCry) doTimeoutRequestCry(timer *time.Timer, req *http.Request) (*http.Response, error) {

    type data struct {
	   resp *http.Response
	   err  error
    }

    done := make(chan data, 1)
    go func() {
	   resp, err := c.httpClient.Do(req)
	   done <- data{resp, err}
    }()

    select {
       case r := <-done:
	   return r.resp, r.err
       case <-timer.C:
	      return nil, errors.New("timeout on reading data from Bittrex API")
    }
}

func (c *clientCry) doCry(method string, ressource string, payload string, authNeeded bool) (response []byte, err error) {
    connectTimer := time.NewTimer(DEFAULT_HTTPCLIENT_TIMEOUT * time.Second)

    var rawurl string        

    nonce := time.Now().UnixNano()
    result :=  fmt.Sprintf("nonce=%d", nonce)
    rawurl = fmt.Sprintf("%s%s/%s?%s", API_BASE_CRY ,API_VERSION_CRY , ressource, result )
    req, err := http.NewRequest(method, rawurl, strings.NewReader(payload))

    sig := ComputeHmac512Hex(API_SECRET_CRY, result)
    
    req.Header.Add("Sign", sig)
    req.Header.Add("Key", API_KEY_CRY )
    
    resp, err := c.doTimeoutRequestCry(connectTimer, req)
    defer resp.Body.Close()
    
    response, err = ioutil.ReadAll(resp.Body)
    fmt.Println(fmt.Sprintf("reponse %s", response), err)
    return response, err
}

func main() {

    crypsy := NewCry(API_KEY_CRY, API_SECRET_CRY)
    r, _ := crypsy.clientCry.doCry("GET", "info", "", true) 
    fmt.Println(r)
}

and my output is :

response {"success":false,"error":["Must be authenticated"]} <nil>

not getting why 需要一些关于如何基于Golang实现一个RESTful API应用的帮助。 im passing the public key and the signature in the header, the signature.. i think im doing it right in the hmac-sha512.
I'm quering the user info url https://www.cryptsy.com/pages/apiv2/user, which as stated in the api site doesn't have any extra query variables so the nonce is the only one needed..

Have googled about restfull api's but haven't been able to find any answer 需要一些关于如何基于Golang实现一个RESTful API应用的帮助。 starting to not let me sleep at night since i think that what im doing is kinda right.. really cant spot the error..

Anyone out there that could try and help me with this?

Thxs a lot 需要一些关于如何基于Golang实现一个RESTful API应用的帮助。

答案1

得分: 1

我看到了result := fmt.Sprintf("%d", nonce)的问题。对应Python代码的代码应该是这样的:

result := fmt.Sprintf("nonce=%d", nonce)

请使用这个修复方案进行检查,好吗?

我还注意到请求发送的方式有很大的区别。Python版本是这样的(链接):

ret = requests.get(link,
                   params=query,
                   headers=headers,
                   verify=False)

但是你的代码没有发送带有添加的nonce等参数的params。我认为应该是这样的:

rawurl := fmt.Sprintf("%s%s/%s?%s", API_BASE_CRY, API_VERSION_CRY, resource, queryStr)

其中queryStr应该包含nonce等参数。

英文:

I see the issue with result := fmt.Sprintf("%d", nonce). The code that corresponds to the Python code should be something like

result :=  fmt.Sprintf("nonce=%d", nonce)

Could you please check it with this fix?

I also can observe a major difference in how the request is sending. The Python version is (link):

        ret = requests.get(link,
                           params=query,
                           headers=headers,
                           verify=False)

but your code is does not send params with added nonce, etc. I think it should be something like

rawurl = fmt.Sprintf("%s%s/%s?%s", API_BASE_CRY ,API_VERSION_CRY , ressource, queryStr)

where queryStr should contain nonce, etc.

huangapple
  • 本文由 发表于 2015年7月30日 07:49:12
  • 转载请务必保留本文链接:https://go.coder-hub.com/31713050.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定