C# 签名 SDK 接入指南
功能说明
C# 签名 SDK 用于通过 AK/SK 鉴权调用美图开放平台 API。SDK 根据请求方法、URL、查询参数、请求头和请求正文生成 SDK-HMAC-SHA256 签名,并返回可由 HttpClient 发送的 HttpRequestMessage。
SDK 负责签名和创建请求;具体接口地址、HTTP 方法及业务字段以目标能力 API 文档为准。
环境要求
- .NET 6.0
- Visual Studio 2022 或 .NET 6 SDK
- 示例工程依赖
System.Net.Http4.3.4
下载与引入
- 下载并解压 C# SDK 与 Demo。
- 将
signer/Signer.cs复制到业务项目。 - 在调用代码中引入命名空间:
using Signature;示例工程的目标框架和包依赖如下:
<TargetFramework>net6.0</TargetFramework>
<PackageReference Include="System.Net.Http" Version="4.3.4" />接口说明
SDK 的公开接口如下:
public Signer(string key, string secret);
public HttpRequestMessage Sign(
string url,
HttpMethod method,
Dictionary<string, string> headers,
string body);参数说明:
| 参数 | 说明 |
|---|---|
key | Access Key(AK) |
secret | Secret Key(SK) |
url | 最终请求 URL,包含完整路径和查询参数 |
method | HTTP 方法,例如 HttpMethod.Post 或 HttpMethod.Get |
headers | 参与签名的请求头;至少应正确设置 Host,JSON 请求设置 Content-Type |
body | 最终请求正文;无正文时传空字符串 "",不能传 null |
返回值为已经包含签名请求头和请求正文的 HttpRequestMessage。Sign 会在传入的 headers 中写入当前 UTC 时间对应的 X-Sdk-Date 和生成的 Authorization。
常用请求头常量:
| 常量 | 请求头 |
|---|---|
Signer.HeaderHost | Host |
Signer.HeaderContentType | Content-Type |
Signer.HeaderContentSha256 | X-Sdk-Content-Sha256 |
Signer.HeaderXDate | X-Sdk-Date |
Signer.HeaderAuthorization | Authorization |
POST 完整调用示例
下面示例调用正式同步任务提交地址:
https://openapi.meitu.com/api/v1/sdk/sync/push
请求体顶层包含 task、task_type、init_images、params 和 sync_timeout。其中 params 必须是 JSON 字符串。示例中的任务、图片和参数值仅用于展示结构,请按目标能力 API 文档替换。
using System;
using System.Collections.Generic;
using System.Net.Http;
using System.Text.Json;
using System.Threading.Tasks;
using Signature;
namespace MainNamespace
{
class Program
{
private static readonly HttpClient Client = new HttpClient
{
Timeout = TimeSpan.FromSeconds(60)
};
static async Task<int> Main()
{
var accessKey = Environment.GetEnvironmentVariable("AIGCP_ACCESS_KEY");
var secretKey = Environment.GetEnvironmentVariable("AIGCP_SECRET_KEY");
if (string.IsNullOrWhiteSpace(accessKey) ||
string.IsNullOrWhiteSpace(secretKey))
{
Console.Error.WriteLine(
"AIGCP_ACCESS_KEY and AIGCP_SECRET_KEY are required");
return 2;
}
var endpoint = new Uri("https://openapi.meitu.com/api/v1/sdk/sync/push");
var payload = new
{
task = "/v1/replace-with-product-task",
task_type = "formula",
init_images = new[]
{
new
{
url = "https://example.com/input.jpg",
profile = new
{
media_profiles = new
{
media_data_type = "url"
},
version = "v1"
}
}
},
@params = JsonSerializer.Serialize(new
{
parameter = new
{
rsp_media_type = "url"
}
}),
sync_timeout = 30
};
var body = JsonSerializer.Serialize(payload);
var headers = new Dictionary<string, string>
{
{ Signer.HeaderContentType, "application/json" },
{ Signer.HeaderHost, endpoint.Authority }
};
var signer = new Signer(accessKey, secretKey);
try
{
using (var request = signer.Sign(
endpoint.AbsoluteUri, HttpMethod.Post, headers, body))
using (var response = await Client.SendAsync(request))
{
var responseBody = await response.Content.ReadAsStringAsync();
Console.WriteLine($"status={(int)response.StatusCode} {response.StatusCode}");
Console.WriteLine(responseBody);
return response.IsSuccessStatusCode ? 0 : 1;
}
}
catch (Exception ex)
{
Console.Error.WriteLine($"request failed: {ex.Message}");
return 1;
}
}
}
}运行示例:
export AIGCP_ACCESS_KEY='<your-access-key>'
export AIGCP_SECRET_KEY='<your-secret-key>'
dotnet run --project csharp.csproj代码只序列化一次最终 body,并将同一个字符串传给 Sign。SDK 使用该字符串的 UTF-8 字节计算摘要,同时使用它创建实际发送的 StringContent,确保签名正文与发送正文一致。task、task_type、图片、params 和 sync_timeout: 30 的实际取值均以目标能力 API 文档为准。
GET 调用示例
/api/v1/sdk/sync/push 是 POST 接口,不能改为 GET。下面代码仅适用于目标 API 文档中明确声明为 GET 的接口;请替换为该接口的真实路径和查询参数。
var endpoint = new Uri("https://openapi.meitu.com/replace-with-get-api-path?page=1");
var headers = new Dictionary<string, string>
{
{ Signer.HeaderContentType, "application/json" },
{ Signer.HeaderHost, endpoint.Authority }
};
var signer = new Signer(accessKey, secretKey);
using (var request = signer.Sign(endpoint.AbsoluteUri, HttpMethod.Get, headers, ""))
using (var response = await Client.SendAsync(request))
{
var responseBody = await response.Content.ReadAsStringAsync();
Console.WriteLine($"status={(int)response.StatusCode} {response.StatusCode}");
Console.WriteLine(responseBody);
}GET 查询参数必须在调用 Sign 前写入最终 URL。无正文时传空字符串 ""。
注意事项
- 生产请求使用目标 API 文档提供的最终 HTTPS URL,并确保
Host与 URL 的Authority一致。 - 在签名前完成方法、URL、查询参数、请求头和正文的构造;签名后不要修改这些内容。
- 当前 SDK 使用 UTF-8
StringContent并发送Content-Type: application/json,请求正文应为最终 JSON 字符串。 - 每次请求或重试都使用新的 headers、重新调用
Sign,并发送新的HttpRequestMessage。 - 保持服务器时间同步,确保 SDK 生成的
X-Sdk-Date有效。 - 仅当目标接口明确支持时,才可在签名前设置
X-Sdk-Content-Sha256: UNSIGNED-PAYLOAD;否则使用默认正文摘要。 - SK 只保存在受信任的服务端,不要写入桌面端、移动端或浏览器代码。
设置 UNSIGNED-PAYLOAD 的方式:
headers[Signer.HeaderContentSha256] = "UNSIGNED-PAYLOAD";常见错误
| 现象 | 检查项 |
|---|---|
| 鉴权失败或签名不匹配 | AK/SK 是否正确;签名后 URL、方法、headers 或 body 是否变化 |
| Host 不匹配 | Signer.HeaderHost 是否使用最终 URL 的 endpoint.Authority |
| 时间错误 | 服务器时间是否同步;是否复用了旧请求或旧签名 |
| POST 正文验签失败 | 是否只生成一次最终 JSON,并将同一个 body 传给 Sign |
| GET 签名失败 | 查询参数是否在签名前写入 URL;无正文时是否传入 "" |
| Content-Type 不一致 | 是否使用 application/json,正文是否为 UTF-8 JSON |
UNSIGNED-PAYLOAD 失败 | 目标接口是否支持该模式,请求头名称和值是否准确 |
| 请求超时或非 2xx | 检查 HTTPS 地址、网络、超时配置,并查看接口返回的状态码和响应体 |