web-dev-qa-db-fra.com

Existe-t-il un moyen de modifier les codes d'état http renvoyés par Amazon API Gateway?

Par exemple, si je veux renvoyer une erreur 400 spécifique pour des paramètres non valides ou peut-être un 201 lorsque l'appel de la fonction lambda a entraîné une création.

J'aimerais avoir différents codes de statut http, mais il semble que la passerelle api renvoie toujours un code de statut 200 même si la fonction lambda renvoie une erreur.

85
MonkeyBonkey

Mise à jour par 20-9-2016

Amazon a finalement rendu cela facile en utilisant le intégration du proxy Lambda . Cela permet à votre fonction Lambda de renvoyer les codes et en-têtes HTTP appropriés:

let response = {
    statusCode: '400',
    body: JSON.stringify({ error: 'you messed up!' }),
    headers: {
        'Content-Type': 'application/json',
    }
};

context.succeed(response);

Dites au revoir au mappage demande/réponse dans la passerelle API!

Option 2

Intégrez une application Express existante à Lambda/API Gateway en utilisant aws-serverless-express .

66

Voici le moyen le plus rapide de retourner des codes d’état HTTP personnalisés et une valeur personnalisée errorMessage:

Dans le tableau de bord API Gateway, procédez comme suit:

  1. Dans le méthode pour votre ressource, cliquez sur réponse de la méthode
  2. Dans la table État HTTP, cliquez sur ajouter une réponse et ajoutez chaque code d'état HTTP que vous souhaitez utiliser.
  3. Dans le méthode pour votre ressource, cliquez sur réponse d'intégration
  4. Ajoutez un réponse d'intégration pour chacun des codes d'état HTTP que vous avez créés précédemment. Assurez-vous que passthrough est coché. Utilisez lambda error regex pour identifier le code d'état à utiliser lorsque vous renvoyez un message d'erreur à partir de votre fonction lambda. Par exemple:

    // Return An Error Message String In Your Lambda Function
    
    return context.fail('Bad Request: You submitted invalid input');
    
    // Here is what a Lambda Error Regex should look like.
    // Be sure to include the period and the asterisk so any text
    // after your regex is mapped to that specific HTTP Status Code
    
    Bad Request: .*
    
  5. Votre route de passerelle API devrait renvoyer ceci:

    HTTP Status Code: 400
    JSON Error Response: 
        {
            errorMessage: "Bad Request: You submitted invalid input"
        }
    
  6. Je ne vois aucun moyen de copier ces paramètres et de les réutiliser pour différentes méthodes. Nous avons donc beaucoup à faire en matière de saisie manuelle redondante!

Mes réponses d'intégration ressemblent à ceci:

aws api gateway lambda error response handling

71
ac360

Pour pouvoir renvoyer un objet d'erreur personnalisé au format JSON, vous devez parcourir plusieurs étapes.

Tout d'abord, vous devez faire échouer Lambda et lui transmettre un objet JSON sous forme de chaîne:

exports.handler = function(event, context) {
    var response = {
        status: 400,
        errors: [
            {
              code:   "123",
              source: "/data/attributes/first-name",
              message:  "Value is too short",
              detail: "First name must contain at least three characters."
            },
            {
              code:   "225",
              source: "/data/attributes/password",
              message: "Passwords must contain a letter, number, and punctuation character.",
              detail: "The password provided is missing a punctuation character."
            },
            {
              code:   "226",
              source: "/data/attributes/password",
              message: "Password and password confirmation do not match."
            }
        ]
    }

    context.fail(JSON.stringify(response));
};

Ensuite, vous configurez le mappage des expressions rationnelles pour chacun des codes d’état que vous souhaitez renvoyer. En utilisant l'objet que j'ai défini ci-dessus, vous devriez configurer cette regex pour 400:

. * "status": 400. *

Enfin, vous configurez un modèle de mappage pour extraire la réponse JSON de la propriété errorMessage renvoyée par Lambda. Le modèle de mappage ressemble à ceci:

$ input.path ('$. errorMessage')

J'ai écrit un article à ce sujet qui décrit plus en détail le flux de réponse de Lambda à API Gateway ici: http://kennbrodhagen.net/2016/03/09/how-to-return-a-custom -error-objet-et-statut-code-de-api-gateway-with-lambda /

17
kennbrodhagen

1) Configurez votre ressource API Gateway à utiliser Intégration Lambda Proxy en cochant la case à cocher intitulée "Utiliser l’intégration Lambda Proxy" sur l’écran "Demande d’intégration" de la définition de ressource de la passerelle API. (Ou définissez-le dans votre configuration cloudformation/terraform/serverless/etc)

2) Modifiez votre code lambda de 2 manières

  • Traitez le event (premier argument de fonction) entrant de manière appropriée. Il ne s'agit plus simplement de la charge nue, il représente l'ensemble de la requête HTTP, y compris les en-têtes, la chaîne de requête et le corps. Échantillon ci-dessous. Le point clé est que les corps JSON seront des chaînes nécessitant un appel explicite JSON.parse(event.body) (n'oubliez pas try/catch autour de cela). Exemple est ci-dessous.
  • Répondez en appelant le rappel avec null, puis un objet de réponse fournissant les détails HTTP, y compris statusCode, body et headers. .
    • body devrait être une chaîne, donc JSON.stringify(payload) au besoin
    • statusCode peut être un nombre
    • headers est un objet de noms d'en-tête en valeurs

Exemple d'argument d'événement Lambda pour l'intégration de proxy

{
    "resource": "/example-path",
    "path": "/example-path",
    "httpMethod": "POST",
    "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": "exampleapiid.execute-api.us-west-2.amazonaws.com",
        "User-Agent": "insomnia/4.0.12",
        "Via": "1.1 9438b4fa578cbce283b48cf092373802.cloudfront.net (CloudFront)",
        "X-Amz-Cf-Id": "oCflC0BzaPQpTF9qVddpN_-v0X57Dnu6oXTbzObgV-uU-PKP5egkFQ==",
        "X-Forwarded-For": "73.217.16.234, 216.137.42.129",
        "X-Forwarded-Port": "443",
        "X-Forwarded-Proto": "https"
    },
    "queryStringParameters": {
        "bar": "BarValue",
        "foo": "FooValue"
    },
    "pathParameters": null,
    "stageVariables": null,
    "requestContext": {
        "accountId": "666",
        "resourceId": "xyz",
        "stage": "dev",
        "requestId": "5944789f-ce00-11e6-b2a2-dfdbdba4a4ee",
        "identity": {
            "cognitoIdentityPoolId": null,
            "accountId": null,
            "cognitoIdentityId": null,
            "caller": null,
            "apiKey": null,
            "sourceIp": "73.217.16.234",
            "accessKey": null,
            "cognitoAuthenticationType": null,
            "cognitoAuthenticationProvider": null,
            "userArn": null,
            "userAgent": "insomnia/4.0.12",
            "user": null
        },
        "resourcePath": "/example-path",
        "httpMethod": "POST",
        "apiId": "exampleapiid"
    },
    "body": "{\n  \"foo\": \"FOO\",\n  \"bar\": \"BAR\",\n  \"baz\": \"BAZ\"\n}\n",
    "isBase64Encoded": false
}

Exemple de forme de réponse de rappel

callback(null, {
  statusCode: 409,
  body: JSON.stringify(bodyObject),
  headers: {
    'Content-Type': 'application/json'
  }
})

Notes - Je crois que les méthodes sur context telles que context.succeed() sont obsolètes. Ils ne sont plus documentés bien qu'ils semblent toujours fonctionner. Je pense que le codage à l'API de rappel est la bonne chose à l'avenir.

9
Peter Lyons

Le moyen le plus simple de procéder consiste à tiliser l’intégration LAMBDA_PROXY . À l'aide de cette méthode, vous n'avez pas besoin de définir de transformation particulière dans le pipeline API Gateway.

Votre objet de retour doit être similaire à l'extrait de code ci-dessous:

module.exports.lambdaHandler = (event, context, done) => {
    // ...
    let response = {
        statusCode: 200, // or any other HTTP code
        headers: {       // optional
             "any-http-header" : "my custom header value"
        },
        body: JSON.stringify(payload) // data returned by the API Gateway endpoint
    };
    done(null, response); // always return as a success
};

Cela présente quelques inconvénients: il faut être particulièrement prudent en matière de gestion des erreurs et coupler votre fonction lambda au point de terminaison API Gateway; Cela dit, si vous ne l'utilisiez pas vraiment ailleurs, le problème ne se poserait pas.

6
Ricardo Nolde

Pour ceux qui ont tout essayé et qui ne pouvaient pas faire ce travail (comme moi), vérifiez le commentaire de thedevkit sur ce post (a sauvé ma journée):

https://forums.aws.Amazon.com/thread.jspa?threadID=192918

En le reproduisant entièrement ci-dessous:

J'ai eu des problèmes avec cela moi-même, et je crois que les personnages newline sont les coupables.

foo. * correspondra aux occurrences de "foo" suivi de tous les caractères SAUF nouvelle ligne. Cela est généralement résolu en ajoutant l'indicateur '/ s', c'est-à-dire "foo. */S", mais l'expression rationnelle de l'erreur Lambda ne semble pas respecter cela.

Comme alternative, vous pouvez utiliser quelque chose comme: foo (. |\N) *

6
Carlos Ballock

Je voulais qu'une erreur de Lambda soit correcte 500, après avoir fait beaucoup de recherches, est venue avec le dessous, ça marche:

Sur LAMBDA

Pour une bonne réponse, je retourne comme ci-dessous:

exports.handler = (event, context, callback) => {
    // ..

    var someData1 =  {
        data: {
            httpStatusCode: 200,
            details: [
                {
                    prodId: "123",
                    prodName: "Product 1"
                },
                {
                    "more": "213",
                    "moreDetails": "Product 2"
                }
            ]
        }
    };
    return callback(null, someData1);
}

Pour une mauvaise réponse, retourne comme ci-dessous

exports.handler = (event, context, callback) => {
    // ..

    var someError1 = {
        error: {
            httpStatusCode: 500,
            details: [
                {
                    code: "ProductNotFound",
                    message: "Product not found in Cart",
                    description: "Product should be present after checkout, but not found in Cart",
                    source: "/data/attributes/product"
                },
                {
                    code: "PasswordConfirmPasswordDoesntMatch",
                    message: "Password and password confirmation do not match.",
                    description: "Password and password confirmation must match for registration to succeed.",
                    source: "/data/attributes/password",
                }
            ]
        }
    };

    return callback(new Error(JSON.stringify(someError1)));
}

sur la passerelle API

Pour une méthode GET, dites GET of/res1/service1:

Through Method Response > Add Response, added 3 responses:
- 200
- 300
- 400

Ensuite,

Through 'Integration Response' > 'Add integration response', create a Regex for 400 errors (client error):

Lambda Error Regex    .*"httpStatusCode":.*4.*

'Body Mapping Templates' > Add mapping template as:  
    Content-Type                 application/json  
    Template text box*           $input.path('$.errorMessage')  


Similarly, create a Regex for 500 errors (server error):

Lambda Error Regex    .*"httpStatusCode":.*5.*

'Body Mapping Templates' > Add mapping template as:  
    Content-Type                 application/json  
    Template text box*           $input.path('$.errorMessage')  

Maintenant, publish/res1/service1, appuyez sur l’URL publiée, qui est connectée à lambda ci-dessus

Utilisé Advanced REST client (ou Postman) chrome plug-in, vous verrez les codes http appropriés, tels que l'erreur de serveur (500) ou 400, au lieu de 200 le code de réponse http pour toutes les demandes où ils ont été donnés. dans "httpStatusCode".

Dans le "tableau de bord" de l'API, dans API Gateway, vous pouvez voir les codes d'état http comme ci-dessous:

400 & 500 errors

6

Voici comment cela est recommandé sur un blog AWS Compute si vous utilisez API Gateway. Vérifier si l'intégration fonctionne avec un appel Lambda direct.

var myErrorObj = {
    errorType : "InternalServerError",
    httpStatus : 500,
    requestId : context.awsRequestId,
    message : "An unknown error has occurred. Please try again."
}
callback(JSON.stringify(myErrorObj));

Pour les invocations Lambda directes, cela semble être la meilleure analyse de solution côté client.

2
spakmad

J'utilise sans serveur 0.5. Voici comment cela fonctionne, pour mon cas

s-function.json:

{
  "name": "temp-err-test",
  "description": "Deployed",
  "runtime": "nodejs4.3",
  "handler": "path/to/handler.handler",
  "timeout": 6,
  "memorySize": 1024,
  "endpoints": [
    {
      "path": "test-error-handling",
      "method": "GET",
      "type": "AWS_PROXY",
      "responses": {
        "default": {
          "statusCode": "200"
        }
      }
    }
  ]
}

handler.js:

'use strict';
function serveRequest(event, context, cb) {
  let response = {
    statusCode: '400',
    body: JSON.stringify({ event, context }),
    headers: {
      'Content-Type': 'application/json',
    }
  };
  cb(null, response);
}
module.exports.handler = serveRequest;
1
Relu Mesaros