我正在使用从 https://www.bitstamp.net/api/ 获得的 OpenAPI 规范 我使用了一个名为 NSWAG 的工具来为我生成客户端。
我似乎无法通过它进行身份验证,不断收到以下错误。我看了很多例子,但我似乎无法让它发挥作用。
响应的 HTTP 状态代码不是预期的 (403)。
状态:403
回应:
{“状态”:“错误”,“原因”:“无效签名”,“代码”:“API0005”}
BitstampClient.cs
public partial class BitstampClient
{
private readonly string key;
private readonly string secret;
private readonly bool isPublicApi;
public BitstampClient(string key, string secret, IHttpClientFactory httpFactory) : this(httpFactory.CreateClient($"BitstampClient-{key}"))
{
this.key = key;
this.secret = secret;
this.isPublicApi = false;
}
public BitstampClient(IHttpClientFactory httpFactory) : this(httpFactory.CreateClient($"BitstampClient-Public"))
{
this.isPublicApi = true;
}
public BitstampClient(string key, string secret) : this(new HttpClient())
{
this.key = key;
this.secret = secret;
}
partial void PrepareRequest(HttpClient client, HttpRequestMessage request, StringBuilder urlBuilder)
{
if (!this.isPublicApi)
{
var xauth = $"{"BITSTAMP"} {this.key}";
var nonce = Guid.NewGuid().ToString(); //DateTime.Now.Ticks;
var time = DateTime.UtcNow.Ticks / TimeSpan.TicksPerMillisecond;
var version = "v2";
var contentType = "application/x-www-form-urlencoded";
var signature = GetXAuthSignature(nonce, this.key, this.secret, "QWERTY");
request.Headers.Add("X-Auth", xauth);
request.Headers.Add("X-Auth-Signature", signature);
request.Headers.Add("X-Auth-Nonce", nonce.ToString());
request.Headers.Add("X-Auth-Timestamp", time.ToString());
request.Headers.Add("X-Auth-Version", version);
request.Content.Headers.ContentType = null;
}
}
private string GetXAuthSignature(string nonce, string key, string secret, string clientId)
{
var msg = $"{nonce}{clientId}{key}";
return ByteArrayToString(SignHMACSHA256(secret, StringToByteArray(msg))).ToUpper();
}
private static byte[] SignHMACSHA256(string key, byte[] data)
{
var hashMaker = new HMACSHA256(Encoding.ASCII.GetBytes(key));
return hashMaker.ComputeHash(data);
}
private static byte[] StringToByteArray(string str)
{
return Encoding.ASCII.GetBytes(str);
}
private static string ByteArrayToString(byte[] hash)
{
return BitConverter.ToString(hash).Replace("-", "").ToLower();
}
}
首先,我要感谢您在这个问题上给我一个良好的开端。通过您提供的示例,我能够开始使用
bitstamp API
..
我最终上的课是:
public partial class BitstampClient
{
private readonly string publicKey;
private readonly string secretKey;
private readonly bool isPublicApi;
public BitstampClient(string publicKey, string secretKey, HttpClient client) : this(client)
{
this.publicKey = publicKey;
this.secretKey = secretKey;
this.isPublicApi = false;
}
partial void PrepareRequest(HttpClient client, HttpRequestMessage request, string url)
{
if (!this.isPublicApi)
{
string apiKey = $"BITSTAMP {this.publicKey}";
string timestamp = DateTimeOffset.UtcNow.ToUnixTimeMilliseconds().ToString();
string nonce = Guid.NewGuid().ToString();
string contentType = string.Empty;
string version = "v2";
string payloadString = string.Empty;
if (request.Method == HttpMethod.Post)
{
request.Content.Headers.ContentType = null;
var payload = request.Content?.ReadAsStringAsync().Result;
if (!string.IsNullOrEmpty(payload))
{
//contentType = "application/x-www-form-urlencoded";
contentType = "application/json";
payloadString = payload;
}
}
url = url.Replace("https://", string.Empty).Replace("http://", string.Empty);
var signature = $"{apiKey}{request.Method}{url}{contentType}{nonce}{timestamp}{version}{payloadString}";
using (var hmac = new HMACSHA256(Encoding.UTF8.GetBytes(secretKey)))
{
byte[] rawHmac = hmac.ComputeHash(Encoding.UTF8.GetBytes(signature));
signature = BitConverter.ToString(rawHmac).Replace("-", "").ToLower();
}
request.Headers.Add("X-Auth", apiKey);
request.Headers.Add("X-Auth-Signature", signature);
request.Headers.Add("X-Auth-Nonce", nonce);
request.Headers.Add("X-Auth-Timestamp", timestamp);
request.Headers.Add("X-Auth-Version", version);
//request.Headers.Add("Content-Type", contentType);
//request.Content = null;
}
}
public virtual Task<TradingPairsInfoResponse[]> GetTradingPairsInfoAsync2()
{
return GetTradingPairsInfoAsync2(System.Threading.CancellationToken.None);
}
[GenerateGenericOverride(nameof(GetTradingPairsInfoAsync))]
public partial Task<TradingPairsInfoResponse[]> GetTradingPairsInfoAsync2(CancellationToken cancellationToken);
}
调用
string key = "";
string secret = "";
var httpClient = new HttpClient();
var client = new BitstampClient(key, secret, httpClient);
var accountBalance = await client.GetAccountBalancesAsync();
var transactions = await client.GetOHLCDataAsync(Step._60, 100, null, null, null, market_symbol: "btcusd");
//var pairs = await client.GetTradingPairsInfoAsync(); // not working
var pairs = await client.GetTradingPairsInfoAsync2();
使用
OpenAPI
的原始 bitstamp
规范时请小心,因为它已损坏。例如,有一个部分:
"OrderTransaction": {
"properties": {
"tid": {
"description": "Transaction ID.",
"example": 1,
"format": "int32",
"readOnly": true,
"type": "integer"
},
"price": {
"description": "Price.",
"example": "100.00",
"readOnly": true,
"type": "string"
},
"{from_currency}": {
"description": "{from_currency} amount.",
"example": "101.00",
"readOnly": true,
"type": "string"
},
这会产生损坏的 C# 客户端代码,如下所示:
/// <summary>
/// {from_currency} amount.
/// </summary>
[System.Text.Json.Serialization.JsonPropertyName("{from_currency}")]
[System.Text.Json.Serialization.JsonIgnore(Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault)]
public string {from_currency
} { get; set; }
/// <summary>
/// {to_currency} amount.
/// </summary>
[System.Text.Json.Serialization.JsonPropertyName("{to_currency}")]
[System.Text.Json.Serialization.JsonIgnore(Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault)]
public string
{ to_currency}
{ get; set; }
您在最初的帖子中没有提到这一点,所以我真的很想知道您是如何解决这个问题的。我最终在生成后手动更新了客户端代码。
另一个需要注意的地方 - 至少端点没有返回正确的数据类型。例如,端点
GetTradingPairsInfoAsync
返回类型为 TradingPairsInfoResponse
的对象,这是根据此处的规范:
"/api/v2/trading-pairs-info/": {
"get": {
"description": "Return trading pairs info.",
"operationId": "GetTradingPairsInfo",
"responses": {
"200": {
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/TradingPairsInfoResponse"
}
}
},
"description": "Get operation"
}
},
"security": [],
"summary": "Trading pairs info",
"tags": [
"Market info"
]
}
},
但是,在调用
await client.GetTradingPairsInfoAsync();
时,您最终会遇到反序列化错误。正确的响应类型是对象数组TradingPairsInfoResponse[]
..我不确定规范中是否还有其他损坏的端点..我通过创建覆盖通用类型的源生成器提出了一些修复。您可以在我的另一篇文章这里阅读有关此主题的更多信息。
请在评论中告诉我您对该规范的体验以及您如何解决上述问题:)