豆包生成C#即梦API HTTP调用实例代码
最近玩即梦AI,文生图,文生视频等等很多玩法都很强大。即梦本身提供了API。官方文档里有Java, Golang, Python, PHP的SDK,官方也推荐使用SDK,调用SDK会比较省事儿。官方也提供了HTTP请求示例代码,但是也只包括Java, Golang, Python, PHP,没有C#。所以就尝试写个C#调用即梦API。调用的难点在于火山引擎API的签名生成。下面介绍一下即梦API的调用过程和使用豆包将Python HTTP示例代码转成C#代码。
1. 注册火山引擎账号,并开通即梦API图片生成服务,开通方式选择免费试用。免费调用时长限额是500秒,并发限额是1。参考新手指南--AI中台公用文档-火山引擎。这个文档中还包含了API访问密钥的生成方式,这个也是使用API必不可少的步骤。注意这个密钥自己妥善保管,不能泄露。
2. 账号开通完成,API访问密钥创建完成就可以开始代码了。我参考了Python的代码,Python代码下载下来,需要替换access_key和secret_key。本地装有Python环境,可以直接跑通。
3. 使用豆包的AI编程,把官方Python代码,贴进豆包提问,请豆包转成C#代码,稍等片刻,C#代码生成完毕。
4. 把C#代码Copy到VS,开始调试。我遇到的问题是:“The request signature we calculated does not match the signature you provided. Check your Secret Access Key and signing method. Consult the service documentation for details.”。是说,request签名,C#里计算的结果和火山引擎服务器计算的结果不一致。
5. 继续调试,通过和Python的签名结果对比,C#函数计算的签名其实是没有问题的,那么问题出在Http request。
6. 进一步调试,发现,这个API的POST request的Header里应该要包含如下内容,这里主要注意Content-Type。
POST Host: billing.volcengineapi.com Content-Type: application/json X-Date: 20250329T180937Z Authorization: HMAC-SHA256 Credential=AKLTYWViMTVmZGYzM2E0NDI5Mzk2MDZjNjFmMjc2MjRjMzg/20250329/cn-beijing/billing/request, SignedHeaders=host;x-date, Signature=5e8480ceea12d0000a23c054151c50dd02c1a7dec835004057d19f13d53a7658
7. 往Http request加header,是通过下面的代码实现的。需要注意的是注释的部分,我调试的错误就是因为AI的代码额外加了Content的UTF8编码,导致服务器端计算的签名和C#计算的不匹配。
var headers = new Dictionary<string, string> { {"X-Date", currentDate}, {"Authorization", authorizationHeader}, {"X-Content-Sha256", payloadHash}, {"Content-Type", contentType} }; ....... foreach (var header in headers) { // 这里加header,Content-Type是加不进去的,它应该要被放在Content的header request.Headers.TryAddWithoutValidation(header.Key, header.Value); } ....... // 加到这里,注意这里一定要只包含Content-Type,不要包含任何编码格式 // AI生成的代码,可能会加上编码格式,导致,发送往服务器的Header Content-Type不完全是 “application/json” request.Content = new StringContent(formattedBody, new System.Net.Http.Headers.MediaTypeHeaderValue(contentType));
8. 最终的调用类完整代码如下。
using Newtonsoft.Json; using System.Security.Cryptography; using System.Text; internal class VolcengineApiClient { private readonly string _accessKey; private readonly string _secretKey; private readonly string _host; private readonly string _region; private readonly string _service; private readonly string _endpoint; private readonly HttpClient _httpClient; /// <summary> /// 初始化火山引擎API客户端 /// </summary> /// <param name="accessKey">访问密钥</param> /// <param name="secretKey">密钥</param> /// <param name="host">API主机地址</param> /// <param name="region">区域</param> /// <param name="service">服务名称</param> public VolcengineApiClient(string accessKey, string secretKey, string host = "visual.volcengineapi.com", string region = "cn-north-1", string service = "cv") { _accessKey = accessKey ?? throw new ArgumentNullException(nameof(accessKey)); _secretKey = secretKey ?? throw new ArgumentNullException(nameof(secretKey)); _host = host ?? throw new ArgumentNullException(nameof(host)); _region = region ?? throw new ArgumentNullException(nameof(region)); _service = service ?? throw new ArgumentNullException(nameof(service)); _endpoint = $"https://{_host}"; _httpClient = new HttpClient(); } /// <summary> /// 发送API请求 /// </summary> /// <param name="method">HTTP方法</param> /// <param name="action">API动作</param> /// <param name="version">API版本</param> /// <param name="bodyParams">请求体参数</param> /// <param name="extraQueryParams">额外的查询参数</param> /// <returns>响应结果</returns> public async Task<string> SendRequestAsync(string method = "POST", string action = "CVProcess", string version = "2022-08-31", object bodyParams = null, Dictionary<string, string> extraQueryParams = null) { // 准备查询参数 var queryParams = new Dictionary<string, string> { {"Action", action}, {"Version", version} }; // 添加额外的查询参数 if (extraQueryParams != null) { foreach (var param in extraQueryParams) { queryParams[param.Key] = param.Value; } } // 格式化查询参数 string formattedQuery = FormatQueryParameters(queryParams); // 准备请求体 string formattedBody = bodyParams != null ? JsonConvert.SerializeObject(bodyParams) : "{}"; // 生成时间戳 DateTime utcNow = DateTime.UtcNow; string currentDate = utcNow.ToString("yyyyMMddTHHmmssZ"); string dateStamp = utcNow.ToString("yyyyMMdd"); // 计算 payload hash string payloadHash = ComputeSha256Hash(formattedBody); // 构建规范请求 string canonicalUri = "/"; string contentType = "application/json"; string canonicalHeaders = $"content-type:{contentType}\n" + $"host:{_host}\n" + $"x-content-sha256:{payloadHash}\n" + $"x-date:{currentDate}\n"; string signedHeaders = "content-type;host;x-content-sha256;x-date"; string canonicalRequest = $"{method}\n{canonicalUri}\n{formattedQuery}\n{canonicalHeaders}\n{signedHeaders}\n{payloadHash}"; // 计算签名 string algorithm = "HMAC-SHA256"; string credentialScope = $"{dateStamp}/{_region}/{_service}/request"; string stringToSign = $"{algorithm}\n{currentDate}\n{credentialScope}\n{ComputeSha256Hash(canonicalRequest)}"; byte[] signingKey = GetSignatureKey(_secretKey, dateStamp, _region, _service); string signature = ComputeHmacSha256(signingKey, stringToSign); // 构建授权头 string authorizationHeader = $"{algorithm} Credential={_accessKey}/{credentialScope}, " + $"SignedHeaders={signedHeaders}, " + $"Signature={signature}"; // 准备请求头 var headers = new Dictionary<string, string> { {"X-Date", currentDate}, {"Authorization", authorizationHeader}, {"X-Content-Sha256", payloadHash}, {"Content-Type", contentType} }; // 构建请求URL string requestUrl = $"{_endpoint}?{formattedQuery}"; Console.WriteLine("\nBEGIN REQUEST++++++++++++++++++++++++++++++++++++"); Console.WriteLine($"Request URL = {requestUrl}"); // 发送请求 HttpResponseMessage response; try { using (var request = new HttpRequestMessage(new HttpMethod(method), requestUrl)) { // 添加请求头 foreach (var header in headers) { request.Headers.TryAddWithoutValidation(header.Key, header.Value); } // 添加请求体 if (method.Equals("POST", StringComparison.OrdinalIgnoreCase) && !string.IsNullOrEmpty(formattedBody)) { request.Content = new StringContent(formattedBody, new System.Net.Http.Headers.MediaTypeHeaderValue(contentType)); } response = await _httpClient.SendAsync(request); } string responseBody = await response.Content.ReadAsStringAsync(); Console.WriteLine("\nRESPONSE++++++++++++++++++++++++++++++++++++"); Console.WriteLine($"Response code: {(int)response.StatusCode}"); Console.WriteLine($"Response body: {responseBody.Replace("\\u0026", "&")}\n"); return responseBody; } catch (Exception ex) { Console.WriteLine($"Error occurred: {ex.Message}"); throw; } } /// <summary> /// 格式化查询参数 /// </summary> private string FormatQueryParameters(Dictionary<string, string> parameters) { if (parameters == null || parameters.Count == 0) return ""; // 按键名排序并拼接 var sortedParams = parameters.OrderBy(p => p.Key); return string.Join("&", sortedParams.Select(p => $"{p.Key}={p.Value}")); } /// <summary> /// 计算SHA256哈希 /// </summary> private string ComputeSha256Hash(string input) { // 创建SHA256实例 using (SHA256 sha256Hash = SHA256.Create()) { // 将输入字符串转换为UTF8字节数组 byte[] inputBytes = Encoding.UTF8.GetBytes(input); // 计算哈希值 byte[] hashBytes = sha256Hash.ComputeHash(inputBytes); // 将字节数组转换为十六进制字符串 StringBuilder builder = new StringBuilder(); for (int i = 0; i < hashBytes.Length; i++) { builder.Append(hashBytes[i].ToString("x2")); // "x2"确保每个字节用两位十六进制表示 } return builder.ToString(); } } /// <summary> /// 计算HMAC-SHA256 /// </summary> private string ComputeHmacSha256(byte[] key, string input) { using (HMACSHA256 hmac = new HMACSHA256(key)) { byte[] hashBytes = hmac.ComputeHash(Encoding.UTF8.GetBytes(input)); return BitConverter.ToString(hashBytes).Replace("-", "").ToLower(); } } /// <summary> /// 获取签名密钥 /// </summary> private byte[] GetSignatureKey(string key, string dateStamp, string regionName, string serviceName) { byte[] kDate = ComputeHmacSha256Bytes(Encoding.UTF8.GetBytes(key), dateStamp); byte[] kRegion = ComputeHmacSha256Bytes(kDate, regionName); byte[] kService = ComputeHmacSha256Bytes(kRegion, serviceName); return ComputeHmacSha256Bytes(kService, "request"); } /// <summary> /// 计算HMAC-SHA256字节数组 /// </summary> private byte[] ComputeHmacSha256Bytes(byte[] key, string input) { using (HMACSHA256 hmac = new HMACSHA256(key)) { return hmac.ComputeHash(Encoding.UTF8.GetBytes(input)); } } }
9. 调用代码
private static async Task Main(string[] args) { try { // 火山官网密钥信息, 注意sk结尾有== string accessKey = "AKL...jI"; string secretKey = "WVR..=="; // 创建客户端实例 var client = new VolcengineApiClient(accessKey, secretKey); // 准备请求参数 var bodyParams = new { req_key = "jimeng_high_aes_general_v21_L", prompt = "stand in forest", return_url = true }; // 发送请求 string response = await client.SendRequestAsync( action: "CVProcess", version: "2022-08-31", bodyParams: bodyParams ); } catch (Exception ex) { Console.WriteLine($"Error: {ex.Message}"); if (ex.InnerException != null) { Console.WriteLine($"Inner Exception: {ex.InnerException.Message}"); } } }
10. 调用输出

至此,即梦API调用成功。

浙公网安备 33010602011771号