英文:
Need some help on how to implement an restfull api app based on golang
问题
我的编码能力有点低
最近我开始学习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的信息,但没有找到答案:(开始让我夜不能寐,因为我认为我所做的事情是正确的...真的找不到错误在哪里..
有没有人可以帮助我解决这个问题?
非常感谢
英文:
My coding skills are a bit low
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
> 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 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 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
答案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.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论