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

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

Amazon crifan 6474浏览 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:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
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";
 
    //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是:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
    <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的:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
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";
 
    //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

代码是:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
private static void cn_browseNodeLookupTest()
{
    string _awsApiVersion = "2011-08-01";
    //string _awsDestination_cn = "ecs.amazonaws.com";
    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";
 
    //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);
}

结果是就可以了:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
    <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:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
private static void cn_browseNodeLookupTest()
{
    //string _awsApiVersion = "2011-08-01";
    //【记录】寻找AWSECommerceService的最新版本
    string _awsApiVersion = "2011-08-02";
 
    //string _awsDestination_cn = "ecs.amazonaws.com";
    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";
 
    //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);
}

结果是

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
    <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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
/*
 * [Function]
 * Implement Amazon AWS API, focus on ItemLookup, ItemSearch, BrowseNodeLookup
 * code based on:
 * Product Advertising API Signed Requests Sample Code - C# REST/QUERY
 * ->
 *
 * [Author]
 * Crifan Li
 *
 * [Date]
 * 2013-06-13
 *
 * [Contact]
 */
 
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
        //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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
/*
 * [Function]
 * Implement Amazon AWS API, focus on ItemLookup, ItemSearch, BrowseNodeLookup
 * code based on:
 * Product Advertising API Signed Requests Sample Code - C# REST/QUERY
 * ->
 *
 * [Author]
 * Crifan Li
 *
 * [Date]
 * 2013-06-20
 *
 * [Contact]
 */
 
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)
        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的最新版本
        //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";
            //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";
             
            //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的最新版本
            //string _awsApiVersion = "2011-08-02";
 
            //string _awsDestination_cn = "ecs.amazonaws.com";
            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";
 
            //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 peterhuang1113@gmail.com
    peter11年前 (2013-11-19)回复
88 queries in 0.576 seconds, using 22.35MB memory