如何在.NET中无SDK调用AWS TranslateText并添加Signature V4签名?
解决.NET环境下直接调用AWS TranslateText REST API的问题
最近在.NET里尝试不用SDK直接调用AWS TranslateText的REST API,踩了好几个坑,跟大家分享下解决过程和可用的代码:
- 一开始以为是Signature V4签名生成错误,用Postman验证后排除了这个可能
- 后来发现问题出在缺少必填的请求头
x-amz-target,它的有效值必须是AWSShineFrontendService_20170701.TranslateText
下面是经过验证可以正常运行的.NET实现代码,替换掉自己的AccessKey和SecretKey就能用:
using Newtonsoft.Json.Linq; using System; using System.Collections.Generic; using System.IO; using System.Linq; using System.Net; using System.Security.Cryptography; using System.Text; using System.Web.Script.Serialization; namespace AWS_TranslateTextTest { class AWS_TranslateText { // Replace this with your own values const string AccessKey = "AKI_ADD_YOUR_ACCESSKEY"; const string SecretKey = "ADD_YOUR_SECRETKEY"; static void Main() { try { string text = "Translate this text from English to German."; string sourceLang = "en"; string targetLang = "de"; string responseText = TranslateText(text, sourceLang, targetLang); JObject json = JObject.Parse(responseText); string translatedText = ""; // Read response from json if (json.ToString().Contains("TranslatedText")) { dynamic obj = json; translatedText = obj.TranslatedText.Value; Console.WriteLine("TranslatedText is: {0}", translatedText); } else { Console.WriteLine("TranslatedText not found in response."); throw new Exception(json.ToString()); } } catch (WebException ex) { Console.WriteLine(ex.Message); if (ex.Response != null) { foreach (string header in ex.Response.Headers) { Console.WriteLine("{0}: {1}", header, ex.Response.Headers[header]); } using (var responseStream = ex.Response.GetResponseStream()) { if (responseStream != null) { using (var streamReader = new StreamReader(responseStream)) { Console.WriteLine(streamReader.ReadToEnd()); } } } } } catch (Exception ex) { Console.WriteLine(ex.Message); } } private static string TranslateText(string text, string sourceLang, string targetLang) { var date = DateTime.UtcNow; const string algorithm = "AWS4-HMAC-SHA256"; const string regionName = "eu-west-1"; const string serviceName = "translate"; const string method = "POST"; const string canonicalUri = "/"; const string canonicalQueryString = ""; const string x_amz_target_header = "AWSShineFrontendService_20170701.TranslateText"; const string contentType = "application/x-amz-json-1.1"; const string host = serviceName + "." + regionName + ".amazonaws.com"; var obj = new { SourceLanguageCode = sourceLang, TargetLanguageCode = targetLang, Text = text }; var requestPayload = new JavaScriptSerializer().Serialize(obj); var hashedRequestPayload = HexEncode(Hash(ToBytes(requestPayload))); var dateStamp = date.ToString("yyyyMMdd"); var requestDate = date.ToString("yyyyMMddTHHmmss") + "Z"; var credentialScope = string.Format("{0}/{1}/{2}/aws4_request", dateStamp, regionName, serviceName); var bytes = ToBytes(requestPayload); var headers = new SortedDictionary<string, string> { {"content-length", bytes.Length.ToString()}, {"content-type", contentType}, {"host", host}, {"x-amz-date", requestDate}, {"x-amz-target", x_amz_target_header} }; string canonicalHeaders = string.Join("\n", headers.Select(x => x.Key.ToLowerInvariant() + ":" + x.Value.Trim())) + "\n"; string signedHeaders = string.Join(";", headers.Select(x => x.Key.ToLowerInvariant())); // Task 1: Create a Canonical Request For Signature Version 4 var canonicalRequest = method + '\n' + canonicalUri + '\n' + canonicalQueryString + '\n' + canonicalHeaders + '\n' + signedHeaders + '\n' + hashedRequestPayload; var hashedCanonicalRequest = HexEncode(Hash(ToBytes(canonicalRequest))); // Task 2: Create a String to Sign for Signature Version 4 var stringToSign = string.Format("{0}\n{1}\n{2}\n{3}", algorithm, requestDate, credentialScope, hashedCanonicalRequest); // Task 3: Calculate the Signature for AWS Signature Version 4 var signingKey = GetSignatureKey(SecretKey, dateStamp, regionName, serviceName); var signature = HexEncode(HmacSha256(signingKey, ToBytes(stringToSign))); // Task 4: Prepare the Authorization Header var authorizationHeader = string.Format("{0} Credential={1}/{2}, SignedHeaders={3}, Signature={4}", algorithm, AccessKey, credentialScope, signedHeaders, signature); // Create the request var request = (HttpWebRequest)WebRequest.Create("https://" + host); request.Method = method; request.ContentType = contentType; request.Headers.Add("x-amz-date", requestDate); request.Headers.Add("x-amz-target", x_amz_target_header); request.Headers.Add("Authorization", authorizationHeader); // Write the payload using (var streamWriter = new StreamWriter(request.GetRequestStream())) { streamWriter.Write(requestPayload); } // Get the response using (var response = (HttpWebResponse)request.GetResponse()) using (var streamReader = new StreamReader(response.GetResponseStream())) { return streamReader.ReadToEnd(); } } // Helper methods for hashing and encoding private static byte[] ToBytes(string str) { return Encoding.UTF8.GetBytes(str); } private static byte[] Hash(byte[] bytes) { using (var sha256 = SHA256.Create()) { return sha256.ComputeHash(bytes); } } private static string HexEncode(byte[] bytes) { var sb = new StringBuilder(); foreach (byte b in bytes) { sb.Append(b.ToString("x2")); } return sb.ToString(); } private static byte[] HmacSha256(byte[] key, byte[] data) { using (var hmac = new HMACSHA256(key)) { return hmac.ComputeHash(data); } } private static byte[] GetSignatureKey(string key, string dateStamp, string regionName, string serviceName) { var kSecret = ToBytes("AWS4" + key); var kDate = HmacSha256(kSecret, ToBytes(dateStamp)); var kRegion = HmacSha256(kDate, ToBytes(regionName)); var kService = HmacSha256(kRegion, ToBytes(serviceName)); var kSigning = HmacSha256(kService, ToBytes("aws4_request")); return kSigning; } } }
注意事项
- 务必替换代码中的
AccessKey和SecretKey为你自己的AWS凭证 - 代码默认使用
eu-west-1区域,如果你的Translate服务部署在其他区域,需要修改regionName变量
内容的提问来源于stack exchange,提问作者Cristina
相关产品推荐
相关产品推荐

