1
votes

obtenir les charges utiles brutes et analysées de la passerelle API AWS à l'intérieur de l'événement?

J'essaie de valider slack les demandes sur les événements que je reçois sur mon lambda, j'utilise actuellement api-gateway avec un backend lambda.

Sur mon serverless.yml, j'ai ceci pour mon gestionnaire d'événements

{ body: 
{ token: 'xxxxxxxxxxxx',
team_id: 'xxxxxxxxxxxx',
api_app_id: 'xxxxxxxxxxxx',
event: 
{ client_msg_id: 'xxxxxxxxxxxx',
type: 'message',
text: 'xxxxxxxxxxxx',
user: 'xxxxxxxxxxxx',
ts: '123456789.000200',
channel: 'xxxxxxxxxxxx',
event_ts: '123456789.000200',
channel_type: 'im' },
type: 'event_callback',
event_id: 'xxxxxxxxxxxx',
event_time: 123456798,
authed_users: [ 'xxxxxxxxxxxx' ] },
method: 'POST',
principalId: '',
stage: 'dev',
cognitoPoolClaims: { sub: '' },
enhancedAuthContext: {},
headers: 
{ Accept: '*/*',
'Accept-Encoding': 'gzip,deflate',
'CloudFront-Forwarded-Proto': 'https',
'CloudFront-Is-Desktop-Viewer': 'true',
'CloudFront-Is-Mobile-Viewer': 'false',
'CloudFront-Is-SmartTV-Viewer': 'false',
'CloudFront-Is-Tablet-Viewer': 'false',
'CloudFront-Viewer-Country': 'US',
'Content-Type': 'application/json',
Host: 'xxxxxxxxxxxx.execute-api.region.amazonaws.com',
'User-Agent': 'Slackbot 1.0 (+https://api.slack.com/robots)',
Via: '1.1 xxxxxxxxxxxx.cloudfront.net (CloudFront)',
'X-Amz-Cf-Id': 'xxxxxxxxxxxx==',
'X-Amzn-Trace-Id': 'Root=xxxxxxxxxxxx',
'X-Forwarded-For': 'xx.xx.xx.xx, xx.xx.xx.xx',
'X-Forwarded-Port': '443',
'X-Forwarded-Proto': 'https',
'X-Slack-Request-Timestamp': '12345678',
'X-Slack-Signature': 'v0=xxxxxxxxxxxx' },
query: {},
path: {},
identity: 
{ cognitoIdentityPoolId: '',
accountId: '',
cognitoIdentityId: '',
caller: '',
sourceIp: 'xx.xx.xx.xx',
accessKey: '',
cognitoAuthenticationType: '',
cognitoAuthenticationProvider: '',
userArn: '',
userAgent: 'Slackbot 1.0 (+https://api.slack.com/robots)',
user: '' },
stageVariables: {} }

J'ai obtenu le contenu du fichier généré via la console d'AWS API Proxy, j'utilise le relais de demande de méthode généré avec une modification qui ajoute simplement le corps brut à la charge utile à livrer au lambda "rawBody": "$input.body", cependant lorsque j'ajoute cette modification, les requêtes cessent d'arriver au lambda et j'obtiens des erreurs lorsque j'envoie des requêtes.

##  See http://docs.aws.amazon.com/apigateway/latest/developerguide/api-gateway-mapping-template-reference.html
##  This template will pass through all parameters including path, querystring, header, stage variables, and context through to the integration endpoint via the body/payload
#set($allParams = $input.params())
{
"body-json" : $input.json('$'),
"rawBody": "$input.body",
"params" : {
#foreach($type in $allParams.keySet())
    #set($params = $allParams.get($type))
"$type" : {
    #foreach($paramName in $params.keySet())
    "$paramName" : "$util.escapeJavaScript($params.get($paramName))"
        #if($foreach.hasNext),#end
    #end
}
    #if($foreach.hasNext),#end
#end
},
"stage-variables" : {
#foreach($key in $stageVariables.keySet())
"$key" : "$util.escapeJavaScript($stageVariables.get($key))"
    #if($foreach.hasNext),#end
#end
},
"context" : {
    "account-id" : "$context.identity.accountId",
    "api-id" : "$context.apiId",
    "api-key" : "$context.identity.apiKey",
    "authorizer-principal-id" : "$context.authorizer.principalId",
    "caller" : "$context.identity.caller",
    "cognito-authentication-provider" : "$context.identity.cognitoAuthenticationProvider",
    "cognito-authentication-type" : "$context.identity.cognitoAuthenticationType",
    "cognito-identity-id" : "$context.identity.cognitoIdentityId",
    "cognito-identity-pool-id" : "$context.identity.cognitoIdentityPoolId",
    "http-method" : "$context.httpMethod",
    "stage" : "$context.stage",
    "source-ip" : "$context.identity.sourceIp",
    "user" : "$context.identity.user",
    "user-agent" : "$context.identity.userAgent",
    "user-arn" : "$context.identity.userArn",
    "request-id" : "$context.requestId",
    "resource-id" : "$context.resourceId",
    "resource-path" : "$context.resourcePath"
    }
}

Pour répondre à certains des commentaires. Si j'utilise l' Use Lambda Proxy integration dans la Integration Request j'obtiens une charge utile comme celle-ci, ce qui est génial, mais j'ai également besoin du corps brut qui n'est pas présent.

          integration: lambda
          passthroughBehavior: "WHEN_NO_TEMPLATE"
          request:
              template:
                application/x-www-form-urlencoded: ${file(aws-api-gateway-form-to-json.ftl)}


1 commentaires

Pour clarifier certaines des choses que j'ai rencontrées, au cas où ils aideraient quelqu'un d'autre, la charge utile brute était simplement json, je n'ai besoin que d'analyser la chaîne en json, puis de la stringifier, cela m'a aidé à résoudre le plus gros problème que j'exécutais dans. En fin de compte, je n'avais pas besoin des modèles personnalisés, le proxy lambda fourni était suffisant.


3 Réponses :


4
votes

Je ne sais pas comment fonctionne sans serveur, mon flux de travail se limite à l'écriture de code localement et au téléchargement de zips, et toute autre configuration est effectuée sur AWS Consolse lui-même.

Je crois que ce que vous recherchez une petite fonctionnalité appelée Lambda Proxy Integration , vous la trouverez sous l'onglet Demande d'intégration sur la passerelle API. Ce qu'il fait, c'est qu'il fournit deux modèles de mappage standard pour la demande et la réponse.

Lorsque vous utilisez l'intégration du proxy Lambda, votre objet événement ressemblera à ceci:

{
    statusCode: Integer,
    headers: HashTable<String, String>,
    body: String
}

La clé "body" est toujours une chaîne, vous devez l'analyser en fonction du type de contenu, c'est-à-dire json ou www-form-encoded ou autre.

Lors de l'utilisation du proxy Lambda, l'objet que vous renvoyez de votre gestionnaire doit suivre un format spécifique selon lequel API Gateway le mappe à la réponse, à savoir:

{
  "resource": "/users/single",
  "path": "/users/single",
  "httpMethod": "GET",
  "headers": {
    "accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8",
    "accept-encoding": "gzip, deflate, br",
    "accept-language": "en-GB,en-US;q=0.9,en;q=0.8",
    "Host": "xxxxxxx.execute-api.xxxx.amazonaws.com",
    "upgrade-insecure-requests": "1",
    "User-Agent": "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/71.0.3578.98 Safari/537.36",
    "X-Amzn-Trace-Id": "Root=xxxxxxxxxxxxxxxxx",
    "X-Forwarded-For": "xx.xx.xx.xx",
    "X-Forwarded-Port": "443",
    "X-Forwarded-Proto": "https"
  },
  "multiValueHeaders": {
    "accept": [
      "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8"
    ],
    "accept-encoding": ["gzip, deflate, br"],
    "accept-language": ["en-GB,en-US;q=0.9,en;q=0.8"],
    "Host": ["xxxxx.execute-api.xxxx.amazonaws.com"],
    "upgrade-insecure-requests": ["1"],
    "User-Agent": [
      "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/71.0.3578.98 Safari/537.36"
    ],
    "X-Amzn-Trace-Id": ["Root=xxxxx"],
    "X-Forwarded-For": ["xx.xx.xx.xx"],
    "X-Forwarded-Port": ["443"],
    "X-Forwarded-Proto": ["https"]
  },
  // this contains the get body
  "queryStringParameters": { "id": "2" },
  "multiValueQueryStringParameters": { "id": ["2"] },
  // This contains pathParams, if you url looks like users/{id}, this object will contain a key called id containing the value from the URL
  "pathParameters": null,
  "stageVariables": null,
  "requestContext": {
    "resourceId": "xxxxx",
    "resourcePath": "/users/single",
    "httpMethod": "GET",
    "extendedRequestId": "xxxxxxx=",
    "requestTime": "xx/xx/xxxx:xx:xx:xx +0000",
    "path": "/dev/users/single",
    "accountId": "642495909037",
    "protocol": "HTTP/1.1",
    "stage": "dev",
    "domainPrefix": "xxxxx",
    "requestTimeEpoch": 1547113372715,
    "requestId": "xx-xx-xx-xxxxx-xxxxxxxxx",
    "identity": {
      "cognitoIdentityPoolId": null,
      "accountId": null,
      "cognitoIdentityId": null,
      "caller": null,
      "sourceIp": "xx.xx.xx.xx",
      "accessKey": null,
      "cognitoAuthenticationType": null,
      "cognitoAuthenticationProvider": null,
      "userArn": null,
      "userAgent": "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/71.0.3578.98 Safari/537.36",
      "user": null
    },
    "domainName": "xxxxxxx.execute-api.xxxxxxx.amazonaws.com",
    "apiId": "xxxx"
  },
  "body": null, //this contains the post body
  "isBase64Encoded": false
}


5 commentaires

Oui, j'utilise bien l'intégration Proxy, sans serveur automatise simplement cette partie. Le modèle que j'ai ajouté est celui que j'utilise pour analyser les événements, si j'utilise simplement celui généré par amazon, cela fonctionnera, mais selon la documentation, $ input.body devrait contenir la requête brute, lorsque je l'ajoute à le modèle, cela ne fonctionne pas, ce que vous avez posté, c'est le corps de la requête analysée, mon problème est avec le modèle d'analyseur.


Pour autant que je sache, l'analyse se produit entre API Gateway et Lambda. Ainsi, l'extrait de code Apache Velocity que vous avez publié s'exécute sur la requête HTTP brute, le convertit en objet ou HashMap ou dictionnaire, puis le transmet à la variable d'événement de la fonction. Si vous utilisez l'intégration de proxy, vous n'avez pas besoin d'ajouter vous-même un modèle de mappage, aws le fait pour vous. Désormais, si vous ne l'avez pas activé, vous pouvez ajouter manuellement un modèle de mappage pour analyser uniquement les détails requis dans un JSON formaté de votre choix à partir du corps de la requête HTTP, qui est écrit dans Apache Velocity.


Si je n'utilise pas de modèle de mappage personnalisé, il ne fournit qu'un corps analysé par json, mais je n'ai pas accès à la charge utile brute, ce qui m'empêche de valider la demande.


Le proxy Lambda envoie toujours le corps sous la forme d'une chaîne brute non analysée. Désactivez peut-être le modèle de mappage supplémentaire, puis enregistrez et inspectez l'objet événement et voyez ce qu'il contient.


J'ai seulement ajouté le modèle de mappage parce que je n'obtenais pas la chaîne brute non analysée.



1
votes

Donc, dans Serverless, il y a quelques options d'intégration ici. https://serverless.com/framework/docs/providers/aws/events/apigateway/#request-templates

Pour clarifier, vous utilisez lambda , vous êtes donc obligé de définir manuellement votre modèle dans API Gateway (que vous avez fourni).

Lorsque vous essayez de faire en sorte que votre serverless.yml utilise le lambda-proxy (également accepté comme aws-proxy ou aws_proxy ), vous dites que tout vous est envoyé dans un format différent qui n'inclut pas le corps brut.

Note latérale: en termes d'intégration LAMBDA_PROXY d'API Gateway, vous devriez obtenir le corps de la requête complet. C'est l'intégration que j'utilise tout le temps (avec ANY méthode sur un chemin de requête {proxy+} ) pour éviter spécifiquement les modèles de mappage. Je ne sais pas si le framework Serverless finit par effectuer une analyse supplémentaire de l' event , mais vous devriez en effet avoir le corps entier dans l'événement donné au gestionnaire de fonctions Lambda. J'ai écrit un framework pour AWS serverless et c'est ce que j'utilise là-bas et je gère des formats de demande autres que JSON. Donc, je sais que vous pouvez obtenir le corps «brut». Avez-vous besoin d'utiliser un framework dans ce cas?

Ok, donc je crois comprendre que vous ne pouvez pas travailler avec l'intégration lambda-proxy et que vous devez recourir au mappage personnalisé dans API Gateway.

J'aurais honnêtement besoin de voir certaines des erreurs de CloudWatch. Je voudrais également voir quelques exemples d'exemples de corps de demande que vous vous attendez à recevoir. Vous avez dit qu'il y avait des erreurs, mais vous n'avez rien publié à leur sujet. Mon hypothèse est qu'il s'agit d'un problème de modèle dans API Gateway. Cela fait quelque temps que je n'ai pas travaillé en détail avec les mappages (encore une fois, l'intégration LAMBDA_PROXY est la voie à suivre), mais laissez-moi vous proposer quelques idées.

N'oubliez pas que $input.body pourrait inclure du JSON qui pourrait gâcher le modèle. Cela créerait une erreur et votre Lambda ne serait jamais déclenché. Dans CloudWatch, vous verriez des choses sur l'impossibilité d'analyser les choses.

Vous pouvez essayer la fonction $util.escapeJavaScript() . Vous pouvez également essayer l'astuce d'utiliser la fonction $util.base64Decode() (cela nécessite l'activation du support binaire sur votre API).

API Gateway peut fonctionner avec des données binaires, représentées sous forme de chaînes en base64, et c'est un moyen de ne pas avoir de problèmes avec le mappage de modèles. Ensuite, par exemple "rawBody": "$util.base64Decode($input.body)" fonctionnerait dans votre modèle de mappage.

Pour activer le support binaire, accédez aux paramètres de l'API API Gateway et vous verrez une section pour Binary Media Types . Vous pouvez y fournir la chaîne de type de contenu de votre choix, même application/json si vous le souhaitez vraiment. Je pense que si vous acceptez JSON, vous pourriez probablement bien l'analyser (potentiellement avec échapper) ... Mais si vous êtes coincé avec quelque chose de bizarre, vous devrez peut-être le faire. Gardez à l'esprit qu'il s'agit d'un paramètre à l'échelle de l'API. Je pense que d'après l'apparence de ce que vous avez partagé, vous utiliseriez simplement application/x-www-form-urlencoded ici, donc les requêtes JSON normales ne seraient pas affectées par exemple.

En bout de ligne, il y a une erreur d'analyse quelque part.


1 commentaires

Désolé pour le retard dans la réponse, j'ai été occupé avec un autre projet et je ne pouvais pas y revenir plus tôt. En ce qui concerne les erreurs, je ne vois en fait aucune erreur sur Cloudwatch sur le lambda (la demande n'arrive jamais là-bas), je n'ai jamais été en mesure de déterminer où sur la passerelle api je peux activer la journalisation. La demande est un message utilisant application / x-www-form-urlencoded, un autre un en-tête avec une signature (qui est une version signée de la charge utile brute, des en-têtes et tout), et le corps codé du formulaire, je vais essayer de tirer un exemple.



0
votes

Je me rends compte que c'est un vieux fil de discussion, mais un an et demi plus tard, j'avais toujours ce problème, et j'ai trouvé de nombreux autres messages SO similaires, alors voici ce qui fonctionne en décembre 2020:

Le paramètre par défaut de la passerelle API consiste à traiter les entrées comme du texte et à les sérialiser en JSON. Cela provoque des problèmes avec les entrées qui ne sont pas du texte, par exemple multipart / form-data avec image / jpeg. La meilleure façon de contourner ce problème est d'activer les types de supports binaires de la passerelle API pour le type de contenu que vous pensez être des données binaires ( https://docs.aws.amazon.com/apigateway/latest/developerguide/api-gateway- payload-encodings.html ). Cela obligera API Gateway à remettre à Lambda une valeur encodée en base64 pour le corps au lieu d'essayer de sérialiser en JSON (il définit également l'en-tête 'isBase64Encoded'). De cette façon, vous pouvez utiliser l'intégration du proxy Lambda sans avoir à vous $util.escapeJavaScript() modèles de mappage ou de la $util.escapeJavaScript() . Le seul changement à traiter dans le code Lambda est le décodage base64 du corps.


0 commentaires