最新消息:20210816 当前crifan.com域名已被污染,为防止失联,请关注(页面右下角的)公众号

【已解决】如何使用Amazon的AWS的中国地区的API,即CN版本的AWS的API

Amazon crifan 6069浏览 0评论

【问题】

别人问的:

我看了您的如下文章,范例中的Browse Node IDs返回的都是amazon.com的资料。我要返回amazon.cn的资料,我用了帮助文档中CN的Browse Node IDs,但是报:“80207071(一个ID)is not a valid value for BrowseNodeId. Please change this value and retry your request.”。
请问,还要改哪里呢?
https://www.crifan.com/continue_optimize_csharp_aws_api_itemlookup/

即,尝试去访问amazon的中国的部分,即CN,即amazon的术语中的endpoint是cn。

 

【解决过程】

1.参考:

http://docs.aws.amazon.com/AWSECommerceService/latest/DG/CHAP_WhatsNew.html

得知,当前用的,虽然不是最新的2011-08-02:

【记录】寻找AWSECommerceService的最新版本

但是,是比较新的,2011-08-01,其已经如上所述:

New Marketplace

The IT (Italy) and CN (China) marketplaces were added.

1 August 2011

即,已经支持CN了。

2.用了默认的US的.com:

        private static void cn_browseNodeLookupTest()
        {
            string _awsApiVersion = "2011-08-01";
            string _awsDestination_cn = "ecs.amazonaws.com";

            SignedRequestHelper helper_cn = new SignedRequestHelper(
                awsAccessKeyId,
                awsSecretKey,
                awsAssociateTag,
                _awsDestination_cn);

            /*
             * Here is an ItemLookup example where the request is stored as a dictionary.
             */
            IDictionary<string, string> reqDict = new Dictionary<string, String>();
            reqDict["Service"] = "AWSECommerceService";
            reqDict["Version"] = _awsApiVersion;
            reqDict["Operation"] = "BrowseNodeLookup";

            //http://docs.aws.amazon.com/AWSECommerceService/latest/DG/BrowseNodeIDs.html
            //for CN: Appliances -> 80207071
            reqDict["BrowseNodeId"] = "80207071";

            reqDict["ResponseGroup"] = "BrowseNodeInfo";

            /*
             * The helper supports two forms of requests - dictionary form and query string form.
             */
            String requestUrl;

            requestUrl = helper_cn.Sign(reqDict);

            //WebRequest request = HttpWebRequest.Create(requestUrl);
            //WebResponse response = request.GetResponse();
            //XmlDocument doc = new XmlDocument();
            //doc.Load(response.GetResponseStream());

            XmlDocument xmlDoc = new XmlDocument();
            HttpWebRequest req = (HttpWebRequest)WebRequest.Create(requestUrl);
            req.Method = "GET";
            HttpWebResponse resp = (HttpWebResponse)req.GetResponse();
            StreamReader sr;
            string charset = "UTF-8";
            Encoding htmlEncoding = Encoding.GetEncoding(charset);
            sr = new StreamReader(resp.GetResponseStream(), htmlEncoding);
            string respHtml = sr.ReadToEnd();
            sr.Close();
            resp.Close();
            xmlDoc.LoadXml(respHtml);

            System.Console.WriteLine(xmlDoc.InnerXml);
        }

返回xml是:

<BrowseNodeLookupResponse xmlns="http://webservices.amazon.com/AWSECommerceService/2011-08-01">
    <OperationRequest>
        <RequestId>c7fada9f-13da-4312-bcfb-10c16eb3d035</RequestId>
        <Arguments>
            <Argument Name="Operation" Value="BrowseNodeLookup"/>
            <Argument Name="Service" Value="AWSECommerceService"/>
            <Argument Name="Signature" Value="bD9/dUvxjPx63vIUHs+WeksSj9MPJUQ0fsLMlFiLbfE="/>
            <Argument Name="AssociateTag" Value="xxx"/>
            <Argument Name="Version" Value="2011-08-01"/>
            <Argument Name="BrowseNodeId" Value="80207071"/>
            <Argument Name="AWSAccessKeyId" Value="xxx"/>
            <Argument Name="Timestamp" Value="2013-06-22T07:39:39Z"/>
            <Argument Name="ResponseGroup" Value="BrowseNodeInfo"/>
        </Arguments>
        <RequestProcessingTime>0.011964</RequestProcessingTime>
    </OperationRequest>
    <BrowseNodes>
        <Request>
            <IsValid>True</IsValid>
            <BrowseNodeLookupRequest>
                <BrowseNodeId>80207071</BrowseNodeId>
                <ResponseGroup>BrowseNodeInfo</ResponseGroup>
            </BrowseNodeLookupRequest>
            <Errors>
                <Error>
                    <Code>AWS.InvalidParameterValue</Code>
                    <Message>80207071 is not a valid value for BrowseNodeId. Please change this value and retry your request.</Message>
                </Error>
            </Errors>
        </Request>
    </BrowseNodes>
</BrowseNodeLookupResponse>

即,和预期的一样,是无效的id。

 

3.又去试了试,用.cn的:

        private static void cn_browseNodeLookupTest()
        {
            string _awsApiVersion = "2011-08-01";
            string _awsDestination_cn = "ecs.amazonaws.cn";

            SignedRequestHelper helper_cn = new SignedRequestHelper(
                awsAccessKeyId,
                awsSecretKey,
                awsAssociateTag,
                _awsDestination_cn);

            /*
             * Here is an ItemLookup example where the request is stored as a dictionary.
             */
            IDictionary<string, string> reqDict = new Dictionary<string, String>();
            reqDict["Service"] = "AWSECommerceService";
            reqDict["Version"] = _awsApiVersion;
            reqDict["Operation"] = "BrowseNodeLookup";

            //http://docs.aws.amazon.com/AWSECommerceService/latest/DG/BrowseNodeIDs.html
            //for CN: Appliances -> 80207071
            reqDict["BrowseNodeId"] = "80207071";

            reqDict["ResponseGroup"] = "BrowseNodeInfo";

            /*
             * The helper supports two forms of requests - dictionary form and query string form.
             */
            String requestUrl;

            requestUrl = helper_cn.Sign(reqDict);

            //WebRequest request = HttpWebRequest.Create(requestUrl);
            //WebResponse response = request.GetResponse();
            //XmlDocument doc = new XmlDocument();
            //doc.Load(response.GetResponseStream());

            XmlDocument xmlDoc = new XmlDocument();
            HttpWebRequest req = (HttpWebRequest)WebRequest.Create(requestUrl);
            req.Method = "GET";
            HttpWebResponse resp = (HttpWebResponse)req.GetResponse();
            StreamReader sr;
            string charset = "UTF-8";
            Encoding htmlEncoding = Encoding.GetEncoding(charset);
            sr = new StreamReader(resp.GetResponseStream(), htmlEncoding);
            string respHtml = sr.ReadToEnd();
            sr.Close();
            resp.Close();
            xmlDoc.LoadXml(respHtml);

            System.Console.WriteLine(xmlDoc.InnerXml);
        }

结果更离谱,返回是看不懂的html内容:

<html><head></head><script type="text/javascript">

var sa = "http://202.102.110.207:8080/"; var pp = "107&pre="+(new Date()).getTime();

var s=String(window.location.href); var host=escape(s.substring(7,s.indexOf(‘/’,7)));

var ref=escape(document.referrer); var su = s+"&host="+host+"&refer="+ref+"&server="+pp;

s = escape(s); function loadfr(){ document.getElementById("fr1").src = sa+"3.htm?AIMT="+su; }

function refreshPage(){ document.location = sa+"2.htm?AIMT="+su; }

if (self.location == top.location){ document.location= sa+"1.htm?AIMT="+su; }

else { refreshPage(); }</script><frameset rows="*,0"><frame id="main" src="">

<frame id="fr1" src=""></frameset><body></body></html>

而不是xml,所以也会导致后面的:

xmlDoc.LoadXml(respHtml);

报错的。

 

4.再去参考:

http://docs.aws.amazon.com/AWSECommerceService/latest/DG/AnatomyOfaRESTRequest.html#EndpointsandWebServices

去试试那个

CN

http://webservices.amazon.cn/onca/xml

https://webservices.amazon.cn/onca/xml

代码是:

        private static void cn_browseNodeLookupTest()
        {
            string _awsApiVersion = "2011-08-01";
            //string _awsDestination_cn = "ecs.amazonaws.com";
            //http://docs.aws.amazon.com/AWSECommerceService/latest/DG/AnatomyOfaRESTRequest.html#EndpointsandWebServices
            string _awsDestination_cn = "webservices.amazon.cn";

            SignedRequestHelper helper_cn = new SignedRequestHelper(
                awsAccessKeyId,
                awsSecretKey,
                awsAssociateTag,
                _awsDestination_cn);

            /*
             * Here is an ItemLookup example where the request is stored as a dictionary.
             */
            IDictionary<string, string> reqDict = new Dictionary<string, String>();
            reqDict["Service"] = "AWSECommerceService";
            reqDict["Version"] = _awsApiVersion;
            reqDict["Operation"] = "BrowseNodeLookup";

            //http://docs.aws.amazon.com/AWSECommerceService/latest/DG/BrowseNodeIDs.html
            //for CN: Appliances -> 80207071
            reqDict["BrowseNodeId"] = "80207071";

            reqDict["ResponseGroup"] = "BrowseNodeInfo";

            /*
             * The helper supports two forms of requests - dictionary form and query string form.
             */
            String requestUrl;

            requestUrl = helper_cn.Sign(reqDict);

            //WebRequest request = HttpWebRequest.Create(requestUrl);
            //WebResponse response = request.GetResponse();
            //XmlDocument doc = new XmlDocument();
            //doc.Load(response.GetResponseStream());

            XmlDocument xmlDoc = new XmlDocument();
            HttpWebRequest req = (HttpWebRequest)WebRequest.Create(requestUrl);
            req.Method = "GET";
            HttpWebResponse resp = (HttpWebResponse)req.GetResponse();
            StreamReader sr;
            string charset = "UTF-8";
            Encoding htmlEncoding = Encoding.GetEncoding(charset);
            sr = new StreamReader(resp.GetResponseStream(), htmlEncoding);
            string respHtml = sr.ReadToEnd();
            sr.Close();
            resp.Close();
            xmlDoc.LoadXml(respHtml);

            System.Console.WriteLine(xmlDoc.InnerXml);
        }

结果是就可以了:

<BrowseNodeLookupResponse xmlns="http://webservices.amazon.com/AWSECommerceService/2011-08-01">
    <OperationRequest>
        <RequestId>88394147-043d-4a5f-bb9d-6958b61b9da8</RequestId>
        <Arguments>
            <Argument Name="Operation" Value="BrowseNodeLookup"/>
            <Argument Name="Service" Value="AWSECommerceService"/>
            <Argument Name="Signature" Value="ovanQqg0HvvAD0d6qdANILaOwQANGBvjmhCKgtpl/kQ="/>
            <Argument Name="AssociateTag" Value="xxx"/>
            <Argument Name="Version" Value="2011-08-01"/>
            <Argument Name="BrowseNodeId" Value="80207071"/>
            <Argument Name="AWSAccessKeyId" Value="xxx"/>
            <Argument Name="Timestamp" Value="2013-06-22T07:42:31Z"/>
            <Argument Name="ResponseGroup" Value="BrowseNodeInfo"/>
        </Arguments>
        <RequestProcessingTime>0.001642</RequestProcessingTime>
    </OperationRequest>
    <BrowseNodes>
        <Request>
            <IsValid>True</IsValid>
            <BrowseNodeLookupRequest>
                <BrowseNodeId>80207071</BrowseNodeId>
                <ResponseGroup>BrowseNodeInfo</ResponseGroup>
            </BrowseNodeLookupRequest>
        </Request>
        <BrowseNode>
            <BrowseNodeId>80207071</BrowseNodeId>
            <Name>大家电</Name>
            <Children>
                <BrowseNode>
                    <BrowseNodeId>80208071</BrowseNodeId>
                    <Name>类别</Name>
                    <IsCategoryRoot>1</IsCategoryRoot>
                </BrowseNode>
                <BrowseNode>
                    <BrowseNodeId>80209071</BrowseNodeId>
                    <Name>特色</Name>
                </BrowseNode>
                <BrowseNode>
                    <BrowseNodeId>81230071</BrowseNodeId>
                    <Name>推荐分类</Name>
                </BrowseNode>
                <BrowseNode>
                    <BrowseNodeId>81231071</BrowseNodeId>
                    <Name>Self Service</Name>
                </BrowseNode>
                <BrowseNode>
                    <BrowseNodeId>81232071</BrowseNodeId>
                    <Name>筛选</Name>
                </BrowseNode>
                <BrowseNode>
                    <BrowseNodeId>83190071</BrowseNodeId>
                    <Name>Archive</Name>
                </BrowseNode>
            </Children>
        </BrowseNode>
    </BrowseNodes>
</BrowseNodeLookupResponse>

不光是可以正常返回对应的BrowseNodeId,而且也是对应的中文的。

 

5.另外,顺带也去测了测2011-08-02版本的API:

        private static void cn_browseNodeLookupTest()
        {
            //string _awsApiVersion = "2011-08-01";
            //【记录】寻找AWSECommerceService的最新版本
            //https://www.crifan.com/find_awsecommerceservice_latest_version
            //http://aws.amazon.com/releasenotes/Product-Advertising-API/7764190151051019
            string _awsApiVersion = "2011-08-02";

            //string _awsDestination_cn = "ecs.amazonaws.com";
            //http://docs.aws.amazon.com/AWSECommerceService/latest/DG/AnatomyOfaRESTRequest.html#EndpointsandWebServices
            string _awsDestination_cn = "webservices.amazon.cn";

            SignedRequestHelper helper_cn = new SignedRequestHelper(
                awsAccessKeyId,
                awsSecretKey,
                awsAssociateTag,
                _awsDestination_cn);

            /*
             * Here is an ItemLookup example where the request is stored as a dictionary.
             */
            IDictionary<string, string> reqDict = new Dictionary<string, String>();
            reqDict["Service"] = "AWSECommerceService";
            reqDict["Version"] = _awsApiVersion;
            reqDict["Operation"] = "BrowseNodeLookup";

            //http://docs.aws.amazon.com/AWSECommerceService/latest/DG/BrowseNodeIDs.html
            //for CN: Appliances -> 80207071
            reqDict["BrowseNodeId"] = "80207071";

            reqDict["ResponseGroup"] = "BrowseNodeInfo";

            /*
             * The helper supports two forms of requests - dictionary form and query string form.
             */
            String requestUrl;

            requestUrl = helper_cn.Sign(reqDict);

            //WebRequest request = HttpWebRequest.Create(requestUrl);
            //WebResponse response = request.GetResponse();
            //XmlDocument doc = new XmlDocument();
            //doc.Load(response.GetResponseStream());

            XmlDocument xmlDoc = new XmlDocument();
            HttpWebRequest req = (HttpWebRequest)WebRequest.Create(requestUrl);
            req.Method = "GET";
            HttpWebResponse resp = (HttpWebResponse)req.GetResponse();
            StreamReader sr;
            string charset = "UTF-8";
            Encoding htmlEncoding = Encoding.GetEncoding(charset);
            sr = new StreamReader(resp.GetResponseStream(), htmlEncoding);
            string respHtml = sr.ReadToEnd();
            sr.Close();
            resp.Close();
            xmlDoc.LoadXml(respHtml);

            System.Console.WriteLine(xmlDoc.InnerXml);
        }

结果是

<BrowseNodeLookupResponse xmlns="http://webservices.amazon.com/AWSECommerceService/2011-08-01">
    <OperationRequest>
        <RequestId>333bac99-0f9b-40b1-8182-1335ccd84c18</RequestId>
        <Arguments>
            <Argument Name="Operation" Value="BrowseNodeLookup"/>
            <Argument Name="Service" Value="AWSECommerceService"/>
            <Argument Name="Signature" Value="GrlWTbhkfwnAYQm4ui7+9BZLDoBclX89ZDZZmU3YZ6Q="/>
            <Argument Name="AssociateTag" Value="xxx"/>
            <Argument Name="Version" Value="2011-08-02"/>
            <Argument Name="BrowseNodeId" Value="80207071"/>
            <Argument Name="AWSAccessKeyId" Value="xxx"/>
            <Argument Name="Timestamp" Value="2013-06-22T07:45:44Z"/>
            <Argument Name="ResponseGroup" Value="BrowseNodeInfo"/>
        </Arguments>
        <RequestProcessingTime>0.002016</RequestProcessingTime>
    </OperationRequest>
    <BrowseNodes>
        <Request>
            <IsValid>True</IsValid>
            <BrowseNodeLookupRequest>
                <BrowseNodeId>80207071</BrowseNodeId>
                <ResponseGroup>BrowseNodeInfo</ResponseGroup>
            </BrowseNodeLookupRequest>
        </Request>
        <BrowseNode>
            <BrowseNodeId>80207071</BrowseNodeId>
            <Name>大家电</Name>
            <Children>
                <BrowseNode>
                    <BrowseNodeId>80208071</BrowseNodeId>
                    <Name>类别</Name>
                    <IsCategoryRoot>1</IsCategoryRoot>
                </BrowseNode>
                <BrowseNode>
                    <BrowseNodeId>80209071</BrowseNodeId>
                    <Name>特色</Name>
                </BrowseNode>
                <BrowseNode>
                    <BrowseNodeId>81230071</BrowseNodeId>
                    <Name>推荐分类</Name>
                </BrowseNode>
                <BrowseNode>
                    <BrowseNodeId>81231071</BrowseNodeId>
                    <Name>Self Service</Name>
                </BrowseNode>
                <BrowseNode>
                    <BrowseNodeId>81232071</BrowseNodeId>
                    <Name>筛选</Name>
                </BrowseNode>
                <BrowseNode>
                    <BrowseNodeId>83190071</BrowseNodeId>
                    <Name>Archive</Name>
                </BrowseNode>
            </Children>
        </BrowseNode>
    </BrowseNodes>
</BrowseNodeLookupResponse>

即,和以前用2011-08-02访问.com的效果一样:

虽然你用的是2011-08-02,但是返回结果却是2011-08-01,直接被服务器无视了。。。

 

【总结】

如果想要正常访问对应的CN版本的AWS的API的话,则要:

1.使用正确的endpoint,此处对于CN的是:

http://webservices.amazon.cn/onca/xml

或:

https://webservices.amazon.cn/onca/xml

 

注意:

(1)之前参考官网的代码,用的是(估计是旧的)基于

ecs.amazonaws.com

而改成CN版本的:

ecs.amazonaws.cn

结果是无效的。

最新的值,都还是参考官网最新的,关于各种endpoint的解释

Anatomy of a REST Request

中的:

CN

http://webservices.amazon.cn/onca/xml

https://webservices.amazon.cn/onca/xml

 

2.然后再用最新的API,此处即2011-08-01。

注意:

(1)即使你用,我之前去调查到的:

【记录】寻找AWSECommerceService的最新版本

最新的2011-08-02,结果服务器也不认,还是返回给你2011-08-01的结果。

 

3. 此处附上完整的代码:

(1)SignedRequestHelper.cs

/*
 * [Function]
 * Implement Amazon AWS API, focus on ItemLookup, ItemSearch, BrowseNodeLookup
 * code based on:
 * Product Advertising API Signed Requests Sample Code - C# REST/QUERY 
 * http://aws.amazon.com/code/Product-Advertising-API/2480
 * ->
 * http://associates-amazon.s3.amazonaws.com/signed-requests/samples/amazon-product-advt-api-sample-csharp-query.zip
 * 
 * [Author]
 * Crifan Li
 * 
 * [Date]
 * 2013-06-13
 * 
 * [Contact]
 * https://www.crifan.com/contact_me/
 */

using System;
using System.Collections.Generic;
using System.Text;
using System.Web;
using System.Security.Cryptography;

namespace AmazonProductAdvtApi
{
    class SignedRequestHelper
    {
        private string endPoint;
        private string akid;
        
        //added by Crifan Li
        //https://affiliate-program.amazon.co.uk/gp/advertising/api/detail/api-changes.html?ie=UTF8&pf_rd_t=501&ref_=amb_link_83388313_2&pf_rd_m=A3P5ROKL5A1OLE&pf_rd_p=&pf_rd_s=assoc-center-1&pf_rd_r=&pf_rd_i=assoc-api-detail-2-v2
        //Product Advertising API Change Details
        //Associate Tag Parameter: Every request made to the API should include a valid Associate Tag. Any request that does not contain a valid Associate Tag will be rejected with an appropriate error message. For details on the Associate Tag parameter, please refer to our Developer guide.
        //-> must add this Associate Tag, otherwise will return:
        //<Error>
        //    <Code>AWS.MissingParameters</Code>
        //    <Message>Your request is missing required parameters. Required parameters include AssociateTag.</Message>
        //</Error>
        private string associateTag;

        private byte[] secret;
        private HMAC signer;

        private const string REQUEST_URI = "/onca/xml";
        private const string REQUEST_METHOD = "GET";

        /*
         * Use this constructor to create the object. The AWS credentials are available on
         * http://aws.amazon.com
         * 
         * The destination is the service end-point for your application:
         *  US: ecs.amazonaws.com
         *  JP: ecs.amazonaws.jp
         *  UK: ecs.amazonaws.co.uk
         *  DE: ecs.amazonaws.de
         *  FR: ecs.amazonaws.fr
         *  CA: ecs.amazonaws.ca
         */
        public SignedRequestHelper(string awsAccessKeyId, string awsSecretKey, string awsAssociateTag, string destination)
        {
            this.endPoint = destination.ToLower();
            this.akid = awsAccessKeyId;
            
            //added by Crifan Li
            this.associateTag = awsAssociateTag;

            this.secret = Encoding.UTF8.GetBytes(awsSecretKey);
            this.signer = new HMACSHA256(this.secret);
        }

        /*
         * Sign a request in the form of a Dictionary of name-value pairs.
         * 
         * This method returns a complete URL to use. Modifying the returned URL
         * in any way invalidates the signature and Amazon will reject the requests.
         */
        public string Sign(IDictionary<string, string> request)
        {
            // Use a SortedDictionary to get the parameters in naturual byte order, as
            // required by AWS.
            ParamComparer pc = new ParamComparer();
            SortedDictionary<string, string> sortedMap = new SortedDictionary<string, string>(request, pc);

            // Add the AWSAccessKeyId and Timestamp to the requests.
            sortedMap["AWSAccessKeyId"] = this.akid;
            
            //added by Crifan Li
            sortedMap["AssociateTag"] = this.associateTag;

            sortedMap["Timestamp"] = this.GetTimestamp();

            // Get the canonical query string
            string canonicalQS = this.ConstructCanonicalQueryString(sortedMap);

            // Derive the bytes needs to be signed.
            StringBuilder builder = new StringBuilder();
            builder.Append(REQUEST_METHOD)
                .Append("\n")
                .Append(this.endPoint)
                .Append("\n")
                .Append(REQUEST_URI)
                .Append("\n")
                .Append(canonicalQS);

            string stringToSign = builder.ToString();
            byte[] toSign = Encoding.UTF8.GetBytes(stringToSign);

            // Compute the signature and convert to Base64.
            byte[] sigBytes = signer.ComputeHash(toSign);
            string signature = Convert.ToBase64String(sigBytes);
            
            // now construct the complete URL and return to caller.
            StringBuilder qsBuilder = new StringBuilder();
            qsBuilder.Append("http://")
                .Append(this.endPoint)
                .Append(REQUEST_URI)
                .Append("?")
                .Append(canonicalQS)
                .Append("&Signature=")
                .Append(this.PercentEncodeRfc3986(signature));

            return qsBuilder.ToString();
        }

        /*
         * Sign a request in the form of a query string.
         * 
         * This method returns a complete URL to use. Modifying the returned URL
         * in any way invalidates the signature and Amazon will reject the requests.
         */
        public string Sign(string queryString)
        {
            IDictionary<string, string> request = this.CreateDictionary(queryString);
            return this.Sign(request);
        }

        /*
         * Current time in IS0 8601 format as required by Amazon
         */
        private string GetTimestamp()
        {
            DateTime currentTime = DateTime.UtcNow;
            string timestamp = currentTime.ToString("yyyy-MM-ddTHH:mm:ssZ");
            return timestamp;
        }

        /*
         * Percent-encode (URL Encode) according to RFC 3986 as required by Amazon.
         * 
         * This is necessary because .NET's HttpUtility.UrlEncode does not encode
         * according to the above standard. Also, .NET returns lower-case encoding
         * by default and Amazon requires upper-case encoding.
         */
        private string PercentEncodeRfc3986(string str)
        {
            str = HttpUtility.UrlEncode(str, System.Text.Encoding.UTF8);
            str = str.Replace("'", "%27").Replace("(", "%28").Replace(")", "%29").Replace("*", "%2A").Replace("!", "%21").Replace("%7e", "~").Replace("+", "%20");

            StringBuilder sbuilder = new StringBuilder(str);
            for (int i = 0; i < sbuilder.Length; i++)
            {
                if (sbuilder[i] == '%')
                {
                    if (Char.IsLetter(sbuilder[i + 1]) || Char.IsLetter(sbuilder[i + 2]))
                    {
                        sbuilder[i + 1] = Char.ToUpper(sbuilder[i + 1]);
                        sbuilder[i + 2] = Char.ToUpper(sbuilder[i + 2]);
                    }
                }
            }
            return sbuilder.ToString();
        }

        /*
         * Convert a query string to corresponding dictionary of name-value pairs.
         */
        private IDictionary<string, string> CreateDictionary(string queryString)
        {
            Dictionary<string, string> map = new Dictionary<string, string>();

            string[] requestParams = queryString.Split('&');

            for (int i = 0; i < requestParams.Length; i++)
            {
                if (requestParams[i].Length < 1)
                {
                    continue;
                }

                char[] sep = { '=' };
                string[] param = requestParams[i].Split(sep, 2);
                for (int j = 0; j < param.Length; j++)
                {
                    param[j] = HttpUtility.UrlDecode(param[j], System.Text.Encoding.UTF8);
                }
                switch (param.Length)
                {
                    case 1:
                        {
                            if (requestParams[i].Length >= 1)
                            {
                                if (requestParams[i].ToCharArray()[0] == '=')
                                {
                                    map[""] = param[0];
                                }
                                else
                                {
                                    map[param[0]] = "";
                                }
                            }
                            break;
                        }
                    case 2:
                        {
                            if (!string.IsNullOrEmpty(param[0]))
                            {
                                map[param[0]] = param[1];
                            }
                        }
                        break;
                }
            }

            return map;
        }

        /*
         * Consttuct the canonical query string from the sorted parameter map.
         */
        private string ConstructCanonicalQueryString(SortedDictionary<string, string> sortedParamMap)
        {
            StringBuilder builder = new StringBuilder();

            if (sortedParamMap.Count == 0)
            {
                builder.Append("");
                return builder.ToString();
            }

            foreach (KeyValuePair<string, string> kvp in sortedParamMap)
            {
                builder.Append(this.PercentEncodeRfc3986(kvp.Key));
                builder.Append("=");
                builder.Append(this.PercentEncodeRfc3986(kvp.Value));
                builder.Append("&");
            }
            string canonicalString = builder.ToString();
            canonicalString = canonicalString.Substring(0, canonicalString.Length - 1);
            return canonicalString;
        }
    }

    /*
     * To help the SortedDictionary order the name-value pairs in the correct way.
     */     
    class ParamComparer : IComparer<string>
    {
        public int Compare(string p1, string p2)
        {
            return string.CompareOrdinal(p1, p2);
        }
    }
}

(2)ItemLookupSample.cs

/*
 * [Function]
 * Implement Amazon AWS API, focus on ItemLookup, ItemSearch, BrowseNodeLookup
 * code based on:
 * Product Advertising API Signed Requests Sample Code - C# REST/QUERY 
 * http://aws.amazon.com/code/Product-Advertising-API/2480
 * ->
 * http://associates-amazon.s3.amazonaws.com/signed-requests/samples/amazon-product-advt-api-sample-csharp-query.zip
 * 
 * [Author]
 * Crifan Li
 * 
 * [Date]
 * 2013-06-20
 * 
 * [Contact]
 * https://www.crifan.com/contact_me/
 */

using System;
using System.Collections.Generic;
using System.Text;
using System.Net;
using System.IO;
using System.Xml;
using System.Xml.XPath;

namespace AmazonProductAdvtApi
{
    class ItemLookupSample
    {
        //【记录】申请AWS的AWSAccessKeyId(AWS Access Key ID)
        //https://www.crifan.com/apply_aws_access_key_id/
        private const string awsAccessKeyId = "change to your access key id";
        private const string awsSecretKey = "change to your secret key";
        private const string awsAssociateTag = "change to your associate tag";
        
        private const string awsDestination = "ecs.amazonaws.com";

        //【记录】寻找AWSECommerceService的最新版本
        //https://www.crifan.com/find_awsecommerceservice_latest_version
        //http://aws.amazon.com/releasenotes/Product-Advertising-API/7764190151051019
        //private const string awsApiVersion = "2011-08-02";
        private const string awsApiVersion = "2011-08-01";
        
        //private const string NAMESPACE = "http://webservices.amazon.com/AWSECommerceService/2009-03-31";
        //private const string NAMESPACE = "http://webservices.amazon.com/AWSECommerceService/2011-08-01";
        private const string NAMESPACE = "http://webservices.amazon.com/AWSECommerceService/" + awsApiVersion;
        //private const string ITEM_ID   = "0545010225";
        


        //private const string ITEM_ID = "B0083PWAPW";
        
        //<Item>
        //    <ASIN>B0083PWAPW</ASIN>
        //    <ParentASIN>B008GGCAVM</ParentASIN>
        //</Item>
        private const string ITEM_ID = "B008GGCAVM";

        private static void itemSearchTest(SignedRequestHelper helper)
        {
            /*
             * Here is an ItemLookup example where the request is stored as a dictionary.
             */
            IDictionary<string, string> reqDict = new Dictionary<string, String>();
            reqDict["Service"] = "AWSECommerceService";
            reqDict["Version"] = awsApiVersion;
            reqDict["Operation"] = "ItemSearch";

            //reqDict["SearchIndex"] = "None";
            reqDict["SearchIndex"] = "HomeGarden";
            reqDict["ResponseGroup"] = "ItemIds";

            //Home & Kitchen ->  Heating, Cooling & Air Quality -> Air Conditioners & Accessories ->  Energy Star Qualified
            //reqDict["BrowseNodeId"] = "3737761";
            reqDict["BrowseNode"] = "3737761";

            reqDict["ItemPage"] = "6";

            String requestUrl = helper.Sign(reqDict);
            WebRequest request = HttpWebRequest.Create(requestUrl);
            WebResponse response = request.GetResponse();
            XmlDocument doc = new XmlDocument();
            doc.Load(response.GetResponseStream());
            //for debug
            System.Console.WriteLine(doc.InnerXml);
        }

        private static void browseNodeLookupTest(SignedRequestHelper helper)
        {
            /*
             * Here is an ItemLookup example where the request is stored as a dictionary.
             */
            IDictionary<string, string> reqDict = new Dictionary<string, String>();
            reqDict["Service"] = "AWSECommerceService";
            reqDict["Version"] = awsApiVersion;
            reqDict["Operation"] = "BrowseNodeLookup";
            //http://docs.aws.amazon.com/AWSECommerceService/latest/DG/BrowseNodeIDs.html
            //2619525011 -> Appliances
            //reqDict["BrowseNodeId"] = "2619525011";

            //<BrowseNodeId>2619525011</BrowseNodeId>
            //<Name>Appliances</Name>
            //<Children>
            //    <BrowseNode>
            //        <BrowseNodeId>2619526011</BrowseNodeId>
            //        <Name>Categories</Name>
            //        <IsCategoryRoot>1</IsCategoryRoot>
            //    </BrowseNode>
            //reqDict["BrowseNodeId"] = "2619526011";
            

            //<Children>
            //    <BrowseNode>
            //        <BrowseNodeId>3737671</BrowseNodeId>
            //        <Name>Air Conditioners</Name>
            //    </BrowseNode>
            //reqDict["BrowseNodeId"] = "3737671";

            //<BrowseNode>
            //    <BrowseNodeId>3737671</BrowseNodeId>
            //    <Name>Air Conditioners &amp; Accessories</Name>
            //    <Children>
            //        <BrowseNode>
            //            <BrowseNodeId>3737761</BrowseNodeId>
            //            <Name>Energy Star Qualified</Name>
            //        </BrowseNode>
            //reqDict["BrowseNodeId"] = "3737761";
            
            //http://docs.aws.amazon.com/AWSECommerceService/latest/DG/BrowseNodeIDs.html
            //for CN: Appliances -> 80207071
            reqDict["BrowseNodeId"] = "80207071";

            reqDict["ResponseGroup"] = "BrowseNodeInfo";

            /*
             * The helper supports two forms of requests - dictionary form and query string form.
             */
            String requestUrl;
            
            requestUrl = helper.Sign(reqDict);
            WebRequest request = HttpWebRequest.Create(requestUrl);
            WebResponse response = request.GetResponse();
            XmlDocument doc = new XmlDocument();
            doc.Load(response.GetResponseStream());

            //for debug
            System.Console.WriteLine(doc.InnerXml);
        }

        private static void cn_browseNodeLookupTest()
        {
            string _awsApiVersion = "2011-08-01";
            //【记录】寻找AWSECommerceService的最新版本
            //https://www.crifan.com/find_awsecommerceservice_latest_version
            //http://aws.amazon.com/releasenotes/Product-Advertising-API/7764190151051019
            //string _awsApiVersion = "2011-08-02";

            //string _awsDestination_cn = "ecs.amazonaws.com";
            //http://docs.aws.amazon.com/AWSECommerceService/latest/DG/AnatomyOfaRESTRequest.html#EndpointsandWebServices
            string _awsDestination_cn = "webservices.amazon.cn";

            SignedRequestHelper helper_cn = new SignedRequestHelper(
                awsAccessKeyId,
                awsSecretKey,
                awsAssociateTag,
                _awsDestination_cn);

            /*
             * Here is an ItemLookup example where the request is stored as a dictionary.
             */
            IDictionary<string, string> reqDict = new Dictionary<string, String>();
            reqDict["Service"] = "AWSECommerceService";
            reqDict["Version"] = _awsApiVersion;
            reqDict["Operation"] = "BrowseNodeLookup";

            //http://docs.aws.amazon.com/AWSECommerceService/latest/DG/BrowseNodeIDs.html
            //for CN: Appliances -> 80207071
            reqDict["BrowseNodeId"] = "80207071";

            reqDict["ResponseGroup"] = "BrowseNodeInfo";

            /*
             * The helper supports two forms of requests - dictionary form and query string form.
             */
            String requestUrl;

            requestUrl = helper_cn.Sign(reqDict);

            //WebRequest request = HttpWebRequest.Create(requestUrl);
            //WebResponse response = request.GetResponse();
            //XmlDocument doc = new XmlDocument();
            //doc.Load(response.GetResponseStream());

            XmlDocument xmlDoc = new XmlDocument();
            HttpWebRequest req = (HttpWebRequest)WebRequest.Create(requestUrl);
            req.Method = "GET";
            HttpWebResponse resp = (HttpWebResponse)req.GetResponse();
            StreamReader sr;
            string charset = "UTF-8";
            Encoding htmlEncoding = Encoding.GetEncoding(charset);
            sr = new StreamReader(resp.GetResponseStream(), htmlEncoding);
            string respHtml = sr.ReadToEnd();
            sr.Close();
            resp.Close();
            xmlDoc.LoadXml(respHtml);

            System.Console.WriteLine(xmlDoc.InnerXml);
        }

        public static void Main()
        {
            SignedRequestHelper helper = new SignedRequestHelper(awsAccessKeyId, awsSecretKey, awsAssociateTag, awsDestination);

            //itemSearchTest(helper);
            //browseNodeLookupTest(helper);

            cn_browseNodeLookupTest();

            /*
             * Here is an ItemLookup example where the request is stored as a dictionary.
             */
            IDictionary<string, string> reqDict = new Dictionary<string, String>();
            reqDict["Service"] = "AWSECommerceService";
            //r1["Version"] = "2009-03-31";
            //r1["Version"] = "2009-03-31";
            //reqDict["Version"] = "2011-08-02";
            reqDict["Version"] = "2011-08-01";
            reqDict["Operation"] = "ItemLookup";
            reqDict["IdType"] = "ASIN";
            
            //reqDict["ItemId"] = ITEM_ID;
            string asin = "B004ZGN6MY";
            //string asin = "B00008OE6I";
            reqDict["ItemId"] = asin;
            
            //reqDict["ResponseGroup"] = "Small";
            //reqDict["ResponseGroup"] = "OfferSummary";
            //reqDict["ResponseGroup"] = "ItemAttributes";
            //reqDict["ResponseGroup"] = "VariationSummary";
            //reqDict["ResponseGroup"] = "Variations";
            reqDict["ResponseGroup"] = "Accessories";

            /*
             * The helper supports two forms of requests - dictionary form and query string form.
             */
            String requestUrl;
            String title;

            requestUrl = helper.Sign(reqDict);
            title = FetchTitle(requestUrl);

            System.Console.WriteLine("Method 1: ItemLookup Dictionary form.");
            System.Console.WriteLine("Title is \"" + title + "\"");
            System.Console.WriteLine();
        }

        private static string FetchTitle(string url)
        {
            try
            {
                WebRequest request = HttpWebRequest.Create(url);
                WebResponse response = request.GetResponse();
                XmlDocument doc = new XmlDocument();
                doc.Load(response.GetResponseStream());

                //for debug
                System.Console.WriteLine(doc.InnerXml);

                XmlNodeList errorMessageNodes = doc.GetElementsByTagName("Message", NAMESPACE);
                if (errorMessageNodes != null && errorMessageNodes.Count > 0)
                {
                    String message = errorMessageNodes.Item(0).InnerText;
                    return "Error: " + message + " (but signature worked)";
                }

                XmlNode titleNode = doc.GetElementsByTagName("Title", NAMESPACE).Item(0);
                string title = titleNode.InnerText;
                
                response.Close();

                return title;
            }
            catch (Exception e)
            {
                System.Console.WriteLine("Caught Exception: " + e.Message);
                System.Console.WriteLine("Stack Trace: " + e.StackTrace);
            }

            return null;
        }
    }
}

然后换成自己的awsAccessKeyId,awsSecretKey,awsAssociateTag,直接拿去用即可。

转载请注明:在路上 » 【已解决】如何使用Amazon的AWS的中国地区的API,即CN版本的AWS的API

发表我的评论
取消评论

表情

Hi,您需要填写昵称和邮箱!

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址

网友最新评论 (2)

  1. Hi, do you have java code for it? I'm not quite familiar with c sharp. I would appreciate your help. my email is [email protected]
    peter11年前 (2013-11-19)回复
88 queries in 0.190 seconds, using 22.30MB memory