web-dev-qa-db-fra.com

AWS API-Gateway communiquant avec SNS

Je construis une API qui sera desservie par des fonctions Lambda, mais j'ai besoin que celles-ci soient asynchrones. Par conséquent, plutôt que de connecter la passerelle API directement à la fonction Lambda, j'utilise "AWS Service Proxy" pour publier Puis envoyez la fonction Lambda à la rubrique SNS appropriée afin qu’elle reçoive les demandes. Voici une image qui illustre le flux:

 enter image description here

J'ai testé à la fois la fonction Lambda en isolation ainsi que la messagerie pub/sous entre SNS et Lambda, mais je me bats avec le transfert API-Gateway to SNS. La documentation est assez légère, mais ce que je suppose en ce moment, c'est que les attributs suivants doivent être envoyés dans la demande POST:

  1. Action : la passerelle API propose de définir cela dans l'interface utilisateur et j'ai mis l'action Publish qui correspond à l'action SNS appropriée.

  2. Message : le corps du message POST doit être un document JSON. Il serait transmis par le client Web et transmis par proxy à SNS via la passerelle.

  3. TopicArn : indique le sujet SNS sur lequel nous publions. Dans ma conception, il s'agirait d'une valeur/point d'extrémité statique, de sorte que je préférerais que le client Web n'ait pas à passer cela aussi, mais s'il était plus facile de le faire, tout irait bien.

J'ai essayé beaucoup de choses mais je suis juste coincé. J'adorerais trouver un bon exemple de code quelque part, mais toute aide serait appréciée.


Je voulais ajouter un peu plus de contexte à ma tentative actuelle:

J'ai essayé de publier mon API et d'utiliser Postman pour tenter d'obtenir une réponse valide. Voici les écrans de postiers (un pour les en-têtes, un pour le corps JSON):

 header variables  json body

Cela entraîne le message d'erreur suivant:

{
   "Error": {
     "Code": "InvalidParameter",
     "Message": "Invalid parameter: TopicArn or TargetArn Reason: no value for required parameter",
     "Type": "Sender"
  },
  "RequestId": "b33b7700-e8a3-58f7-8ebe-39e4e62b02d0"
}

l'erreur semble indiquer que le paramètre TopicArn n'est pas envoyé à SNS, mais j'ai inclus les éléments suivants dans API-Gateway:

 enter image description here

13
ken

J'ai finalement réussi à faire fonctionner cela après avoir travaillé avec le support AWS. Voici ma solution:

  • Tout d’abord, même si vous envoyez une POST, vous pourrez pas être en mesure d’envoyer un message JSON dans le corps du message comme prévu.
  • Au lieu de cela, vous devez URL Encoder le JSON et le transmettre en tant que paramètre de requête.
  • Rappelez-vous également que le JSON que vous envoyez doit commencer par un objet racine de default qui, dans SNS-world, désigne le "canal par défaut".
  • Ensuite, Lambda détecte éventuellement l’événement SNS. Vous devez également éliminer beaucoup de bruit pour obtenir votre message JSON. Pour cela, j'ai créé la fonction suivante que j'utilise dans ma fonction Lambda:

/**
 * When this is run in AWS it is run "through" a SNS
 * event wconfig.ich adds a lot of clutter to the event data,
 * this tests for SNS data and normalizes when necessary
 */
function abstractSNS(e) {
  if (e.Records) {
    return JSON.parse(decodeURIComponent(e.Records[0].Sns.Message)).default;
  } else {
    return e;
  }
}

/**
 * HANDLER
 * This is the entry point for the lambda function
 */
exports.handler = function handler(event, context) {
  parent.event = abstractSNS(event);

6
ken

Je suis de l'équipe Api Gateway.

Je crois qu'il existe quelques formats pour la requête HTTP à l'API de publication, mais voici celui que j'ai utilisé en premier:

Région AWS us-west-2 

AWS Service sns 

Sous-domaine AWS

Méthode HTTP POST 

Action Publier 

== chaînes de requête ==

Sujet 'foo'
Message 'bar'
TopicArn 'arn: aws: sns: us-west-2: xxxxxxxxxxxx: test-api'

Cela a fonctionné pour moi pour publier un message.

Faites-moi savoir si vous avez d'autres problèmes.

Jack

8
Jack Kohn - AWS

Je le ferais comme:

WebApp -> Gateway -> Lambda (Utilisez Boto3 pour publier dans SNS) -> SNS -> Lambda

Je pense que les choses seront plus simples.

1
Anand Bajpai

Je ne fais que spéculer (je n'ai pas essayé cela moi-même), mais je pense que vous n'envoyez pas le message correctement ...

D'après la documentation d'AWS ici ( http://docs.aws.Amazon.com/sns/latest/api/API_Publish.html ), vous devez POST le message dans ce qui semble être le codage application/x-www-form-urlencoded comme ceci:

POST http://sns.us-west-2.amazonaws.com/ HTTP/1.1
...
Action=Publish
&Message=%7B%22default%22%3A%22This+is+the+default+Message%22%2C%22APNS_SANDBOX%22%3A%22%7B+%5C%22aps%5C%22+%3A+%7B+%5C%22alert%5C%22+%3A+%5C%22You+have+got+email.%5C%22%2C+%5C%22badge%5C%22+%3A+9%2C%5C%22sound%5C%22+%3A%5C%22default%5C%22%7D%7D%22%7D
&TargetArn=arn%3Aaws%3Asns%3Aus-west-2%3A803981987763%3Aendpoint%2FAPNS_SANDBOX%2Fpushapp%2F98e9ced9-f136-3893-9d60-776547eafebb
&SignatureMethod=HmacSHA256
&AWSAccessKeyId=AKIAIOSFODNN7EXAMPLE
&SignatureVersion=2
&Version=2010-03-31
&Signature=vmqc4XRupKAxsDAdN4j4Ayw5LQljXMps3kss4bkDfCk%3D
&Timestamp=2013-07-18T22%3A44%3A09.452Z
&MessageStructure=json

C'est-à-dire que le corps du message ressemble à la manière dont un navigateur encoderait les données de formulaire. Votre message peut être au format JSON, mais doit tout de même être encodé comme s'il s'agissait d'un champ de formulaire (une analogie complexe :)).

En outre, selon la documentation des paramètres communs ( http://docs.aws.Amazon.com/sns/latest/api/CommonParameters.html ), vous disposez d’un certain nombre de champs obligatoires supplémentaires (la clé d’accès habituelle, signature et ainsi de suite).

Vous n'avez pas spécifié la langue dans laquelle vous écrivez votre passerelle API - vous pouvez utiliser un kit de développement logiciel (SDK) AWS, au lieu d'essayer de composer manuellement les demandes REST.

1
xpa1492

Vous pouvez utiliser API Gateway pour appeler votre fonction Lambda de manière asynchrone en la configurant en tant que proxy de service AWS. La configuration est fondamentalement la même que celle que vous voyez dans cet exemple GitHub , à l'exception du fait que l'URI de l'appel Lambda est remplacé par /invoke-async/ au lieu de/invoke /

1
Stefano Buliani

Si quelqu'un cherche toujours une solution au problème initial, il est possible de transmettre un corps de requête JSON par proxy à une rubrique SNS via la passerelle API uniquement. 

Créez la passerelle comme décrit par Ken ci-dessus. Ensuite, envoyez simplement le corps au paramètres de requête de la demande d'intégration }. Vous pouvez également coder en dur Subject, TopicArn, etc. ici, ou mapper ceux du corps de la requête à l'aide de JsonPath .

Par exemple: 

{
   //body
   "topic": "arn:aws:sns:1234567:topic"
}

Peut être associé à un en-tête en tant que: 

method.request.body.topic
0
Garrett