août
2010
Avez-vous déjà voulu requêter les web services d’amazon afin de récupérer le titre d’un film ou d’un livre ?
Si oui, vous vous êtes sûrement déjà confrontés au brouillard de tous les web services amazon afin de trouver le bon, et après il faut encore trouver son nom ( car ils ont tendance à changer ) et à trouver quelques bons exemples de codes encore valable, voire la bonne adresse du WSDL.
Après une petite après-midi de recherche, j’ai pu trouver ce que je voulais et arriver au résultat voulu : récupérer toutes les informations d’un produit à partir de son code EAN (code-barre).
Après avoir passé quelques heures sur le site d’amazon et m’être aperçu que tous les exemples ne sont plus valable, je me suis tourné vers google. Et après quelques recherches infructueuses, j’ai trouvé la perle : le code-source et le guide pour réaliser le même code.
Merci Oren Trutner : https://flyingpies.wordpress.com/2009/08/01/17/
Au passage, il faut se créer un compte pour utiliser les web services d’Amazon. Vous pouvez vous en créer un gratuitement à cette adresse : http://aws.amazon.com/ en cliquant sur le lient « Create an AWS Account » en haut à droite. Une fois la création effectuée, allez dans Account->Security Credentials et gardez sous la main l’Access Key ID et la Secret Access Key dans la partie Access Credentials, vous en aurez besoin par la suite.
Au niveau du code, au début rien de bien compliqué : le WSDL est dispo à l’adresse http://webservices.amazon.com/AWSECommerceService/AWSECommerceService.wsdl.
Il suffit de créer une nouveau projet dans Visual Studio et d’ajouter une référence de service avec cette uri, en asynchrone ou non selon vos goûts.
On obtient donc de jolies classes de proxy, que l’on peut donc utiliser afin de requêter Amazon.
{
// your Amazon ID's
private const string accessKeyId = "AccessKeyId";
private const string secretKey = "SecretAccessKey";
// the program starts here
static void Main(string[] args)
{
// create a WCF Amazon ECS client
AWSECommerceServicePortTypeClient client = new AWSECommerceServicePortTypeClient(
new BasicHttpBinding(),
new EndpointAddress("http://webservices.amazon.com/onca/soap?Service=AWSECommerceService"));
// prepare an ItemSearch request
ItemSearchRequest request = new ItemSearchRequest();
request.SearchIndex = "All";
request.Keywords = "3700259800655";
request.ResponseGroup = new String[] { "Large" };
ItemSearch itemSearch = new ItemSearch();
itemSearch.Request = new ItemSearchRequest[] { request };
itemSearch.AWSAccessKeyId = accessKeyId;
// issue the ItemSearch request
ItemSearchResponse response = client.ItemSearch(itemSearch);
// write out the results
foreach (var item in response.Items[0].Item)
{
Console.WriteLine(item.ItemAttributes.Title);
}
Console.ReadLine();
Pour information, la liste de tous les SearchIndex est ici : http://docs.amazonwebservices.com/AWSEcommerceService/4-0/ApiReference/SearchIndexValues.html. Il faut tout de même lui ajouter All, qui correspond à une recherche sur toutes les catégories.
Mais le code précédent ne fonctionne pas. Pas avant d’avoir bidouillé un peu. Car ce qu’Amazon ne dit pas sur son site (ou alors il fallait chercher pendant encore quelques heures), c’est toute la partie authentification des messages SOAP qu’il faut mettre en place.
Trois classes vont rendre cela possible :
- AmazonSigningMessageInspector : implémente IClientMessageInspector et permet de modifier la requête avant son envoi. Elle va donc rajouter les headers.
- AmazonSigningEndpointBehavior : implémente IEndpointBehavior et permet d’ajouter le bon MessageInspector au bon moment.
- AmazonHeader : hérite de MessageHeader et est utilisée par le MessageInspector pour ajouter les headers.
Voilà la partie du code de AmazonSigningMessageInspector ajoutant les headers :
{
// prepare the data to sign
string operation = Regex.Match(request.Headers.Action, "[^/]+$").ToString();
DateTime now = DateTime.UtcNow;
string timestamp = now.ToString("yyyy-MM-ddTHH:mm:ssZ");
string signMe = operation + timestamp;
byte[] bytesToSign = Encoding.UTF8.GetBytes(signMe);
// sign the data
byte[] secretKeyBytes = Encoding.UTF8.GetBytes(secretKey);
HMAC hmacSha256 = new HMACSHA256(secretKeyBytes);
byte[] hashBytes = hmacSha256.ComputeHash(bytesToSign);
string signature = Convert.ToBase64String(hashBytes);
// add the signature information to the request headers
request.Headers.Add(new AmazonHeader("AWSAccessKeyId", accessKeyId));
request.Headers.Add(new AmazonHeader("Timestamp", timestamp));
request.Headers.Add(new AmazonHeader("Signature", signature));
return null;
}
Cela permettra de mettre en place un header de ce type :
xmlns:aws="http://security.amazonaws.com/doc/2007-01-01/">
<aws:AWSAccessKeyId>YOURACCESSKEYIDHEREX</aws:AWSAccessKeyId>
<aws:Timestamp>2009-08-15T23:59:59Z</aws:Timestamp>
<aws:Signature>SZf1CHmQnrZbsrC13hCZS061ywsEXAMPLE</aws:Signature>
</soap:Header>
Il suffit ensuite de modifier la façon d’appeler le web service pour que tout roule :
BasicHttpBinding binding = new BasicHttpBinding(BasicHttpSecurityMode.Transport);
binding.MaxReceivedMessageSize = int.MaxValue;
AWSECommerceServicePortTypeClient client = new AWSECommerceServicePortTypeClient(
binding,
new EndpointAddress("http://webservices.amazon.com/onca/soap?Service=AWSECommerceService"));
// add authentication to the ECS client
client.ChannelFactory.Endpoint.Behaviors.Add(new AmazonSigningEndpointBehavior(accessKeyId, secretKey));
Et afin de pouvoir « localiser » les web services, j’ai créée une petite classe AmazonEndpointAdress recensant toutes les adresses des endpoint des web services Amazon, accessibles ici : http://docs.amazonwebservices.com/AWSECommerceService/latest/DG/index.html?AnatomyOfaRESTRequest.html.
Bien entendu, votre fichier App.config doit comporter tous les endpoint correspondant.
Et au final, le code d’appel ressemble à celle-ci :
binding,
new EndpointAddress(AmazonEndpointAdresses.FR));
Et voilà !
Vous pouvez maintenant rechercher n’importe quel produit sur n’importe quel site d’Amazon !
Les sources sont disponibles ici : http://laedit.ftp-developpez.com/CSharp/Amazon/AmazonProductAdvtApiWcfSample.zip.