所以我试图测试来自Gemini的经过验证的回复。我得到了一个"响应",但不是我期望的......它以html字符串形式返回,而不是"一系列余额"就像我应该得到的API suggests一样。我之前没有使用过HttpClient,所以这里有双重学习曲线。不确定我是否有错误信息,或者我是否查看了错误的方法/属性?
我对如何从Gemini返回此数组感到有些困惑。
我有API Key和Secret,因为这只是在沙盒上。帐户有大约1,000,000个虚假资产。
JSON字符串
{"request":"/v1/balances","nonce":1477275934999}
JSON / Payload的Base64编码
eyJyZXF1ZXN0IjoiL3YxL2JhbGFuY2VzIiwibm9uY2UiOjE0NzcyNzU5MzQ5OTl9
发布消息
Method: POST, RequestUri: 'https://api.sandbox.gemini.com/', Version: 1.1, Content: System.Net.Http.StringContent, Headers:
{
X-GEMINI-APIKEY: L6qDKmQZcmLVDTd5zK9S
X-GEMINI-PAYLOAD: eyJyZXF1ZXN0IjoiL3YxL2JhbGFuY2VzIiwibm9uY2UiOjE0NzcyNzU5MzQ5OTl9
X-GEMINI-SIGNATURE: DBA15C6B2FFA602F9323E0180A14BABEA018640324E313D6C8BBAE9F8872B06325B75F6C507C55435982D600EE5DDF57
Content-Type: text/plain; charset=utf-8
}
回复字符串:(似乎总是'工作',即使我有不正确的请求,我也没注意到一点)
StatusCode: 200, ReasonPhrase: 'OK', Version: 1.1, Content: System.Net.Http.StreamContent, Headers:
{
Connection: keep-alive
Age: 6605
X-Cache: Hit from cloudfront
X-Amz-Cf-Id: Ae3ps4mhbjIzGCrgFr43dq0ICZvnUmPKpeo1miv4ufDFJkJtNZy8GA==
Date: Sat, 22 Oct 2016 22:55:51 GMT
ETag: "5dc6bbdccdd2100c4ca4b0155402db35"
Server: AmazonS3
Via: 1.1 cae81d5ff1d682b28f2deabdd94777d4.cloudfront.net (CloudFront)
Content-Length: 172
Content-Type: text/html
Last-Modified: Fri, 29 Jul 2016 18:31:06 GMT
}
Response.Content - 不是我的预期......
<html>
<head>
<script>
window.location.replace("/rest-api/" + window.location.search + window.location.hash);
</script>
</head>
</html>
代码(C#.NET 4.5)
using System;
using System.Net.Http;
using System.Security.Cryptography;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using Gemini.API;
using Newtonsoft.Json;
namespace HodlBot
{
class Program
{
public const int TIME_INTERVAL_IN_MILLISECONDS = 3000;
private static Timer m_timer = null;
private static void Main(string[] args)
{
var source = new CancellationTokenSource();
Console.CancelKeyPress += (s, e) =>
{
e.Cancel = true;
source.Cancel();
};
m_timer = new System.Threading.Timer(Tick, source, TIME_INTERVAL_IN_MILLISECONDS, Timeout.Infinite);
source.Token.WaitHandle.WaitOne();
}
private async static void Tick(object p_state)
{
Console.Clear();
var c = (CancellationTokenSource)p_state;
/* All requests must contain a nonce, a number that will never be repeated and must increase between requests. This is to prevent an attacker who has
* captured a previous request from simply replaying that request. We recommend using a millisecond-level timestamp. The nonce need only be
* increasing with respect to the session that the message is on.*/
long nonce = DateTimeOffset.Now.ToUnixTimeMilliseconds();
string jObject = JsonConvert.SerializeObject(new Balance(nonce));
await POST(jObject);
// Re-Prime Event
if(!c.Token.IsCancellationRequested)
m_timer.Change(TIME_INTERVAL_IN_MILLISECONDS, Timeout.Infinite);
}
private static async Task POST(string jsonText)
{
// Sign Payload
Console.WriteLine(jsonText);
string payload = System.Convert.ToBase64String((Encoding.UTF8.GetBytes(jsonText)));
Console.WriteLine(payload);
HMACSHA384 hmac = new HMACSHA384(Encoding.UTF8.GetBytes("LixWGpx3h89D8B4uQvfxd8hy5N1")); //My API SECRET
byte[] hash = hmac.ComputeHash(Encoding.UTF8.GetBytes(payload));
string hexHash = BitConverter.ToString(hash).Replace("-", "");
using(var httpClient = new HttpClient())
using(var stringContent = new StringContent("", Encoding.UTF8, "text/plain"))
using(var r = new HttpRequestMessage(HttpMethod.Post, "https://api.sandbox.gemini.com"))
{
r.Headers.Add("X-GEMINI-APIKEY", "L6qDKmQZcmLVDTd5zK9S");
r.Headers.Add("X-GEMINI-PAYLOAD", payload);
r.Headers.Add("X-GEMINI-SIGNATURE", hexHash);
r.Content = stringContent;
Console.WriteLine(r.ToString());
var response = await httpClient.SendAsync(r);
if(response.IsSuccessStatusCode)
{
Console.WriteLine(response.ToString());
var content = await response.Content.ReadAsStringAsync();
Console.WriteLine(content);
}
}
}
}
}
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Newtonsoft.Json;
namespace Gemini.API
{
public class BasicRequest
{
[JsonProperty("request", Order = 1)]
public string Request { get; internal set; }
[JsonProperty("nonce", Order = 2)]
public long Nonce { get; internal set; }
public BasicRequest(string request, long nonce)
{
Request = request;
Nonce = nonce;
}
}
public class HeartBeat : BasicRequest
{
public HeartBeat(long nonce) : base("/v1/heartbeat", nonce) { }
}
public class Balance : BasicRequest
{
public Balance(long nonce) : base("/v1/balances", nonce) { }
}
}