Gibt es eine Möglichkeit, die von Amazon API Gateway zurückgegebenen http-Statuscodes zu ändern?

95

Zum Beispiel, wenn ich einen bestimmten 400-Fehler für ungültige Parameter oder vielleicht einen 201-Fehler zurückgeben möchte, wenn der Lambda-Funktionsaufruf zu einer Erstellung führte.

Ich hätte gerne verschiedene http-Statuscodes, aber es sieht so aus, als ob das API-Gateway immer einen 200-Statuscode zurückgibt, selbst wenn die Lambda-Funktion einen Fehler zurückgibt.

MonkeyBonkey
quelle
2
Es sieht also so aus, als hätte ich das Problem, dass ich einen benutzerdefinierten Fehlertyp zurückgegeben habe - was dazu führt, dass der Regex errorMessage nicht richtig funktioniert. Wenn Sie eine Standardzeichenfolge in der Fehlerantwort von Lambda zurückgeben, funktioniert die folgende Lösung. Die Rückgabe Ihres eigenen benutzerdefinierten Fehlerobjekts funktioniert jedoch nicht.
MonkeyBonkey
Meine Lösung bestand darin, von Serveless Version 0.5 auf 1.0 zu wechseln. Außerdem verwende ich die Antwort aus der Serveless-Dokumentation und gebe den statusCode im Antwortobjekt als Eigenschaft an. Hoffe es hilft
Relu Mesaros

Antworten:

79

Update per 20-9-2016

Amazon hat dies mithilfe der Lambda Proxy-Integration endlich einfach gemacht . Dadurch kann Ihre Lambda-Funktion die richtigen HTTP-Codes und -Header zurückgeben:

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

context.succeed(response);

Verabschieden Sie sich von der Anforderungs- / Antwortzuordnung im API-Gateway!

Option 2

Integrieren Sie eine vorhandene Express-App mit aws-serverless-express in Lambda / API Gateway .

Eric Eijkelenboom
quelle
Ich kann es nicht integrieren, ich meine, ich erhalte den Status 200 und die erstellte Antwort (den erstellten Fehler). Vermisse ich etwas Wie sieht die "s-function.json" aus?
Relu Mesaros
Das einfachste Beispiel finden Sie in AWSs eigenem Lambda-Entwurf namens microservice-http-endpoint (in der AWS Lambda-Konsole). Sie erwähnen "s-function.json", was so klingt, als würden Sie das Serverless Framework ( serverless.com ) verwenden. Dies ist ein ganz anderes Biest und sollte nicht mit aws-serverless-express oder 'raw' Lambda / API Gateway verwechselt werden. Meine Antwort beschreibt nicht, wie dies mit dem Serverless Framework funktioniert.
Eric Eijkelenboom
7
Für alle, die sich fragen, kann dies auch mit dem neuen callbackStil erreicht werden. Tu es einfach callback(null, {statusCode: 200, body: 'whatever'}).
Widdershin
1
@Sushil Ja, Sie geben den JSON einfach wie in der obigen Antwortvariablen zurück.
Unclemeat
8
@Sushil Ich habe dies in Python mit LambdaProxyIntegration und Rückkehr gelöstreturn { "isBase64Encoded": True, "statusCode": 200, "headers": { }, "body": "" }
Jithu R Jacob
74

Hier ist der schnellste Weg, um benutzerdefinierte HTTP-Statuscodes und einen benutzerdefinierten zurückzugeben errorMessage:

Gehen Sie im API Gateway-Dashboard wie folgt vor:

  1. Klicken Sie in der Methode für Ihre Ressource auf Methodenantwort
  2. Klicken Sie in der HTTP-Statustabelle auf Antwort hinzufügen und fügen Sie jeden HTTP-Statuscode hinzu, den Sie verwenden möchten.
  3. Klicken Sie in der Methode für Ihre Ressource auf Integrationsantwort
  4. Fügen Sie für jeden zuvor erstellten HTTP- Statuscode eine Integrationsantwort hinzu . Stellen Sie sicher, dass die Eingabe-Passthrough aktiviert ist. Verwenden Sie den Lambda-Fehler-Regex , um zu ermitteln, welcher Statuscode verwendet werden soll, wenn Sie eine Fehlermeldung von Ihrer Lambda-Funktion zurückgeben. Beispielsweise:

    // 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. Ihre API-Gateway-Route sollte Folgendes zurückgeben:

    HTTP Status Code: 400
    JSON Error Response: 
        {
            errorMessage: "Bad Request: You submitted invalid input"
        }
    
  6. Ich sehe keine Möglichkeit, diese Einstellungen zu kopieren und für verschiedene Methoden wiederzuverwenden, daher müssen wir viel nervige redundante manuelle Eingaben vornehmen!

Meine Integrationsantworten sehen folgendermaßen aus:

aws api gateway lambda fehlerantwort handhabung

ac360
quelle
3
Es sieht also so aus, als ob mein Problem darin bestand, dass der Regex-Trigger nie funktioniert hat, da ich ein Fehlerobjekt von Lambda in der Fail-Methode und nicht nur eine Zeichenfolge zurückgebe. zBreturn context.fail(new Error('bad one'))
MonkeyBonkey
7
@kalisjoshua Ich habe kürzlich einen ziemlich detaillierten Beitrag zur Fehlerbehandlung mit API Gateway / Lambda veröffentlicht: jayway.com/2015/11/07/…
Carl
9
Was entspricht context.fail für Python Lambda?
Routeburn
1
Für Python: Eine Ausnahme auslösen. Siehe docs.aws.amazon.com/lambda/latest/dg/python-exceptions.html
devxoul
1
Gibt es keine Möglichkeit, den Statuscode in fehlerfreien Antworten zu ändern? Was ist, wenn ich "201 Created" zusammen mit dem erstellten Objekt senden möchte?
Ben Davis
18

Um ein benutzerdefiniertes Fehlerobjekt als JSON zurückgeben zu können, müssen Sie durch einige Rahmen springen.

Zuerst müssen Sie das Lambda nicht bestehen und ihm ein stringifiziertes JSON-Objekt übergeben:

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));
};

Als Nächstes richten Sie die Regex-Zuordnung für jeden Statuscode ein, den Sie zurückgeben möchten. Mit dem oben definierten Objekt würden Sie diesen regulären Ausdruck für 400 einrichten:

. * "Status": 400. *

Schließlich richten Sie eine Zuordnungsvorlage ein, um die JSON-Antwort aus der von Lambda zurückgegebenen errorMessage-Eigenschaft zu extrahieren. Die Zuordnungsvorlage sieht folgendermaßen aus:

$ input.path ('$. errorMessage')

Ich habe einen Artikel dazu geschrieben, der detaillierter geht und den Antwortfluss von Lambda zum API-Gateway hier erklärt: http://kennbrodhagen.net/2016/03/09/how-to-return-a-custom-error-object -und-Status-Code-vom-API-Gateway-mit-Lambda /

kennbrodhagen
quelle
@kennbrodhagen Kennen Sie API Gateway und Java Lambdas? Ich verwende eine Art der gleichen reg exp, und es funktioniert nicht für mich. Ich benutze. * StatusCode ": 422. *
Perimosh
@Perimosh Lesen Sie diesen Artikel, in dem erklärt wird, wie dies mit Java-Ausnahmen gemacht wird: aws.amazon.com/blogs/compute/…
kennbrodhagen
10

1) Konfigurieren Sie Ihre API-Gateway-Ressource für die Verwendung der Lambda-Proxy-Integration, indem Sie das Kontrollkästchen "Lambda-Proxy-Integration verwenden" im Bildschirm "Integrationsanforderung" der API-Gateway-Ressourcendefinition aktivieren. (Oder definieren Sie es in Ihrer Cloudformation / Terraform / Serverless / etc-Konfiguration)

2) Ändern Sie Ihren Lambda-Code auf zwei Arten

  • Verarbeiten Sie den Eingang event(1. Funktionsargument) entsprechend. Es ist nicht mehr nur die bloße Nutzlast, sondern stellt die gesamte HTTP-Anforderung dar, einschließlich Header, Abfragezeichenfolge und Text. Beispiel unten. Der entscheidende Punkt ist, dass JSON-Körper Zeichenfolgen sind, die einen expliziten JSON.parse(event.body)Aufruf erfordern (vergessen Sie try/catchdas nicht). Beispiel ist unten.
  • Respond durch den Rückruf mit null dann eine Antwort Objekt aufrufen , die die HTTP - Details einschließlich bietet statusCode, bodyund headers.
    • bodysollte eine Zeichenfolge sein, tun Sie dies JSON.stringify(payload)nach Bedarf
    • statusCode kann eine Zahl sein
    • headers ist ein Objekt von Headernamen zu Werten

Beispiel für ein Lambda-Ereignisargument für die Proxy-Integration

{
    "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
}

Beispiel für eine Rückrufantwortform

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

Anmerkungen - Ich glaube, die Methoden auf contextsolche context.succeed()sind veraltet. Sie sind nicht mehr dokumentiert, obwohl sie noch zu funktionieren scheinen. Ich denke, dass das Codieren in die Callback-API die richtige Sache für die Zukunft ist.

Peter Lyons
quelle
Das funktioniert nicht. Ich erhalte immer noch den Status 200 mit dieser gesamten Antwortausgabe. Die API kann nicht so eingestellt werden, dass sie tatsächlich den Status 409 zurückgibt
Andy N
7

Ich wollte, dass ein Fehler von Lambda richtig ist. 500 Fehler, nachdem ich viel recherchiert hatte, fand ich das Folgende, das funktioniert:

Auf LAMBDA

Für eine gute Antwort kehre ich wie folgt zurück:

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);
}

Für eine schlechte Antwort kehren Sie wie folgt zurück

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)));
}

Auf dem API-Gateway

Sagen Sie für eine GET-METHODE GET von / res1 / service1:

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

Dann,

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')  

Veröffentlichen Sie nun / res1 / service1 und klicken Sie auf die veröffentlichte URL, die mit dem obigen Lambda verbunden ist

Wenn Sie das Chrome-Plugin des Advanced REST-Clients (oder Postman) verwenden, werden anstelle aller 200 http-Antwortcodes für alle Anforderungen, die in "httpStatusCode" angegeben wurden, die richtigen http-Codes wie Serverfehler (500) oder 400 angezeigt.

Im 'Dashboard' der API im API-Gateway können Sie die folgenden http-Statuscodes sehen:

400 & 500 Fehler

Manohar Reddy Poreddy
quelle
7

Der einfachste Weg, dies zu tun, ist die Verwendung der LAMBDA_PROXY-Integration . Mit dieser Methode müssen keine speziellen Transformationen in die API-Gateway-Pipeline festgelegt werden.

Ihr Rückgabeobjekt muss dem folgenden Snippet ähnlich sein:

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
};

Es hat einige Nachteile: Sie müssen besonders vorsichtig mit der Fehlerbehandlung umgehen und Ihre Lambda-Funktion an den API-Gateway-Endpunkt koppeln. Das heißt, wenn Sie es nicht wirklich irgendwo anders verwenden würden, ist es kein so großes Problem.

Ricardo Nolde
quelle
6

Für diejenigen, die alles versucht haben, was mit dieser Frage zu tun hat, und diese Arbeit nicht zum Laufen bringen konnten (wie ich), lesen Sie den Kommentar zu diesem Beitrag (rettete meinen Tag):

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

Wiedergabe ganz unten:

Ich hatte selbst Probleme damit und ich glaube, dass die Newline-Charaktere der Schuldige sind.

foo. * stimmt mit dem Auftreten von "foo" überein, gefolgt von Zeichen mit Ausnahme der Zeilenumbruch. Normalerweise wird dies durch Hinzufügen des '/ s'-Flags, dh "foo. * / S", gelöst, aber der Lambda-Fehler-Regex scheint dies nicht zu berücksichtigen.

Alternativ können Sie Folgendes verwenden: foo (. | \ N) *

Carlos Ballock
quelle
erstaunlicher Fund! Es hat mir nur Stunden erspart, meinen Kopf zu schlagen! Und es ist alles andere als offensichtlich.
Mirko Vukušić
Mirko, ich bin froh, dass es dir geholfen hat!
Carlos Ballock
2

So wird es in einem AWS Compute-Blog empfohlen, wenn Sie API Gateway verwenden. Überprüfen Sie, ob die Integration mit dem direkten Lambda-Aufruf funktioniert.

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

Für direkte Lambda-Aufrufe scheint dies die beste Lösung zu sein, die auf der Clientseite analysiert wird.

spakmad
quelle
Was wäre, wenn das Beispiel ein Lambda-zu-Lambda-Anruf wäre? würde das so genannte Lambda immer noch zurückkehren? und wie kann ich diesen httpStatus auf dem aufrufenden Lambda lesen.
Rod
1

Ich verwende serverless 0.5. So funktioniert es in meinem Fall

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;
Relu Mesaros
quelle
1

Wenn Sie keinen Proxy verwenden möchten, können Sie diese Vorlage verwenden:

#set($context.responseOverride.status =  $input.path('$.statusCode'))
George Ogden
quelle