มีวิธีเปลี่ยนรหัสสถานะ http ที่ส่งคืนโดย Amazon API Gateway หรือไม่


97

ตัวอย่างเช่นถ้าฉันต้องการส่งคืนข้อผิดพลาด 400 เฉพาะสำหรับพารามิเตอร์ที่ไม่ถูกต้องหรืออาจเป็น 201 เมื่อการเรียกฟังก์ชันแลมบ์ดาทำให้เกิดการสร้าง

ฉันต้องการมีรหัสสถานะ http ที่แตกต่างกัน แต่ดูเหมือนว่า api gateway จะส่งคืนรหัสสถานะ 200 เสมอแม้ว่าฟังก์ชัน lambda จะส่งคืนข้อผิดพลาดก็ตาม


2
ดูเหมือนว่าปัญหาที่ฉันพบคือฉันส่งคืนประเภทข้อผิดพลาดที่กำหนดเองซึ่งทำให้ regex errorMessage ทำงานไม่ถูกต้อง การส่งคืนสตริงมาตรฐานในการตอบกลับล้มเหลวจากแลมบ์ดาจะทำให้โซลูชันด้านล่างใช้งานได้ - การส่งคืนอ็อบเจ็กต์ข้อผิดพลาดที่คุณกำหนดเองจะไม่ได้ผล
MonkeyBonkey

วิธีแก้ปัญหาของฉันคือเปลี่ยนจาก Serveless เวอร์ชัน 0.5 เป็น 1.0 นอกจากนี้ฉันกำลังใช้การตอบกลับจากเอกสาร Serveless โดยระบุ statusCode ในวัตถุตอบกลับเป็นคุณสมบัติ หวังว่ามันจะช่วยได้
Relu Mesaros

คำตอบ:


81

อัพเดทวันที่ 20-9-2559

Amazon ในที่สุดก็ทำง่ายนี้ใช้บูรณาการแลมบ์ดาพร็อกซี่ สิ่งนี้ช่วยให้ฟังก์ชัน Lambda ของคุณส่งคืนรหัสและส่วนหัว HTTP ที่เหมาะสม:

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

context.succeed(response);

บอกลาการแมปคำขอ / การตอบกลับใน API Gateway!

ทางเลือกที่ 2

บูรณาการเป็น app เอ็กซ์เพรสที่มีอยู่กับแลมบ์ดา / API เกตเวย์ใช้AWS-serverless ด่วน


1
ฉันไม่สามารถผสานรวมได้ฉันหมายความว่าฉันได้รับ 200 สถานะและการตอบสนองที่สร้างขึ้น ฉันพลาดอะไรไปรึเปล่า ? "s-function.json" มีลักษณะอย่างไร
Relu Mesaros

7
สำหรับทุกคนที่สงสัยสิ่งนี้สามารถทำได้โดยใช้callbackรูปแบบใหม่ callback(null, {statusCode: 200, body: 'whatever'})เพียงแค่ทำ
Widdershin

1
@unclemeat ฉันมีคำถามเหมือนกัน คุณคิดออกหรือไม่? วิธีการทำใน python
Sushil

1
@Sushil ใช่คุณส่งคืน JSON เหมือนในตัวแปรการตอบสนองด้านบน
unclemeat

8
@Sushil ฉันได้แก้ไขสิ่งนี้ใน Python ด้วย LambdaProxyIntegration และส่งคืนreturn { "isBase64Encoded": True, "statusCode": 200, "headers": { }, "body": "" }
Jithu R Jacob

74

นี่เป็นวิธีที่เร็วที่สุดในการส่งคืนรหัสสถานะ HTTP แบบกำหนดเองและแบบกำหนดเองerrorMessage:

ในแดชบอร์ด API Gateway ให้ทำดังต่อไปนี้:

  1. ในวิธีการสำหรับทรัพยากรของคุณคลิกที่วิธีการตอบสนอง
  2. ในตารางสถานะ HTTPคลิกเพิ่มการตอบกลับและเพิ่มรหัสสถานะ HTTP แต่ละรายการที่คุณต้องการใช้
  3. ในวิธีการสำหรับทรัพยากรของคุณคลิกที่การตอบสนองการรวม
  4. เพิ่มการตอบกลับการรวมสำหรับรหัสสถานะ HTTP แต่ละรายการที่คุณสร้างไว้ก่อนหน้านี้ ตรวจสอบให้แน่ใจว่าได้ทำเครื่องหมายการป้อนข้อมูล passthroughแล้ว ใช้lambda error regexเพื่อระบุว่าควรใช้รหัสสถานะใดเมื่อคุณส่งคืนข้อความแสดงข้อผิดพลาดจากฟังก์ชัน lambda ของคุณ ตัวอย่างเช่น:

    // 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. เส้นทาง API Gateway ของคุณควรส่งคืนสิ่งนี้:

    HTTP Status Code: 400
    JSON Error Response: 
        {
            errorMessage: "Bad Request: You submitted invalid input"
        }
    
  6. ฉันไม่เห็นวิธีที่จะคัดลอกการตั้งค่าเหล่านี้และใช้ซ้ำสำหรับวิธีการต่างๆดังนั้นเราจึงมีการป้อนข้อมูลซ้ำซ้อนที่น่ารำคาญให้ทำ!

คำตอบในการผสานรวมของฉันมีลักษณะดังนี้:

การจัดการการตอบสนองข้อผิดพลาด aws api gateway lambda


3
ดังนั้นดูเหมือนว่าปัญหาของฉันคือทริกเกอร์ regex ไม่เคยทำงานตั้งแต่ฉันส่งคืนวัตถุข้อผิดพลาดจากแลมบ์ดาในวิธีการล้มเหลวแทนที่จะเป็นเพียงสตริง เช่นreturn context.fail(new Error('bad one'))
MonkeyBonkey

7
@kalisjoshua ฉันเพิ่งเผยแพร่โพสต์โดยละเอียดเกี่ยวกับการจัดการข้อผิดพลาดกับ API Gateway / Lambda: jayway.com/2015/11/07/…
Carl

9
บริบทที่เทียบเท่ากับ Python Lambda's คืออะไร?
routeburn

1
สำหรับ python: เพิ่มข้อยกเว้น ดูdocs.aws.amazon.com/lambda/latest/dg/python-exceptions.html
devxoul

1
ไม่มีวิธีใดในการเปลี่ยนรหัสสถานะในการตอบสนองที่ไม่ใช่ข้อผิดพลาด? จะเกิดอะไรขึ้นถ้าฉันต้องการส่ง "201 Created" พร้อมกับวัตถุที่สร้างขึ้น
Ben Davis

18

เพื่อให้สามารถส่งคืนออบเจ็กต์ข้อผิดพลาดที่กำหนดเองเป็น JSON คุณต้องข้ามห่วงสองสามครั้ง

ขั้นแรกคุณต้องล้มเหลว Lambda และส่งผ่านวัตถุ JSON แบบสตริง:

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

จากนั้นคุณตั้งค่าการแมป regex สำหรับรหัสสถานะแต่ละรหัสที่คุณต้องการส่งคืน การใช้วัตถุที่ฉันกำหนดไว้ข้างต้นคุณจะตั้งค่า regex นี้สำหรับ 400:

. * "สถานะ": 400. *

สุดท้ายคุณตั้งค่าแม่แบบการจับคู่เพื่อแยกการตอบสนอง JSON จากคุณสมบัติ errorMessage ที่ส่งคืนโดย Lambda เทมเพลตการทำแผนที่มีลักษณะดังนี้:

$ input.path ('$. errorMessage')

ฉันเขียนบทความเกี่ยวกับเรื่องนี้ซึ่งมีรายละเอียดเพิ่มเติมและอธิบายขั้นตอนการตอบสนองจาก Lambda ไปยัง API Gateway ที่นี่: http://kennbrodhagen.net/2016/03/09/how-to-return-a-custom-error-object -and-status-code-from-api-gateway-with-lambda /


@kennbrodhagen คุณรู้จัก API Gateway และ Java Lambdas ไหม ฉันใช้ reg exp แบบเดียวกันและมันไม่ได้ผลสำหรับฉัน ฉันใช้. * statusCode ": 422. *
Perimosh

@Perimosh ดูบทความนี้ที่อธิบายวิธีการทำสิ่งนี้ด้วยข้อยกเว้นของ Java: aws.amazon.com/blogs/compute/…
kennbrodhagen

10

1)กำหนดค่าทรัพยากร API Gateway ของคุณเพื่อใช้Lambda Proxy Integrationโดยทำเครื่องหมายที่ช่อง"Use Lambda Proxy integration"ในหน้าจอ "Integration Request" ของข้อกำหนดทรัพยากร API Gateway (หรือกำหนดใน cloudformation / terraform / serverless / etc config)

2)เปลี่ยนรหัสแลมบ์ดาของคุณได้ 2 วิธี

  • ประมวลผลขาเข้าevent(อาร์กิวเมนต์ของฟังก์ชันที่ 1) อย่างเหมาะสม ไม่ใช่แค่เพย์โหลดเปล่าอีกต่อไป แต่แสดงถึงคำขอ HTTP ทั้งหมดรวมถึงส่วนหัวสตริงข้อความค้นหาและเนื้อหา ตัวอย่างด้านล่าง ประเด็นสำคัญคือเนื้อความ JSON จะเป็นสตริงที่ต้องมีการJSON.parse(event.body)เรียกที่ชัดเจน(อย่าลืมtry/catchสิ่งนั้น) ตัวอย่างอยู่ด้านล่าง
  • ตอบสนองโดยการเรียกโทรกลับด้วย null แล้ววัตถุการตอบสนองที่ให้รายละเอียดของ HTTP รวมทั้งstatusCode, และ bodyheaders
    • bodyควรเป็นสตริงดังนั้นให้ทำJSON.stringify(payload)ตามต้องการ
    • statusCode สามารถเป็นตัวเลข
    • headers เป็นออบเจ็กต์ของชื่อส่วนหัวของค่า

ตัวอย่างอาร์กิวเมนต์เหตุการณ์ Lambda สำหรับการรวมพร็อกซี

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

รูปร่างการตอบกลับการโทรกลับตัวอย่าง

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

หมายเหตุ - ฉันเชื่อว่าวิธีการcontextดังกล่าวcontext.succeed()ถูกเลิกใช้แล้ว เอกสารเหล่านี้ไม่ได้รับการจัดทำเป็นเอกสารอีกต่อไปแม้ว่าจะดูเหมือนยังใช้งานได้ ฉันคิดว่าการเข้ารหัสไปยัง API การเรียกกลับเป็นสิ่งที่ถูกต้องในอนาคต


1
สิ่งนี้ใช้ไม่ได้ ฉันยังคงได้รับ 200 สถานะกลับมาพร้อมกับผลลัพธ์การตอบกลับทั้งหมดนี้ ไม่สามารถตั้งค่า api ให้ส่งคืนสถานะ 409 ได้จริง
Andy N

7

ฉันต้องการให้ข้อผิดพลาดจากแลมบ์ดาเป็นข้อผิดพลาด 500 ที่เหมาะสมหลังจากทำการวิจัยจำนวนมากได้ผลดังต่อไปนี้:

บน LAMBDA

สำหรับการตอบรับที่ดีฉันจะกลับมาดังต่อไปนี้:

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

สำหรับการตอบสนองที่ไม่ดีให้ส่งกลับตามด้านล่าง

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

บน API Gateway

สำหรับ GET METHOD ให้พูดว่า GET of / res1 / service1:

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

จากนั้น

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

ตอนนี้เผยแพร่ / res1 / service1 กด URL ที่เผยแพร่ซึ่งเชื่อมต่อกับแลมด้าด้านบน

ใช้ปลั๊กอิน Chrome ไคลเอนต์ขั้นสูง (หรือบุรุษไปรษณีย์) คุณจะเห็นรหัส http ที่ถูกต้องเช่นข้อผิดพลาดของเซิร์ฟเวอร์ (500) หรือ 400 แทนที่จะเป็นรหัสตอบกลับ 200 http สำหรับคำขอทั้งหมดที่ได้รับใน "httpStatusCode"

จาก 'แดชบอร์ด' ของ API ใน API เกตเวย์เราสามารถดูรหัสสถานะ http ดังต่อไปนี้:

ข้อผิดพลาด 400 & 500


7

วิธีที่ง่ายที่สุดในการทำเช่นนี้คือการใช้บูรณาการ LAMBDA_PROXY เมื่อใช้วิธีนี้คุณไม่จำเป็นต้องมีการแปลงพิเศษใด ๆ เพื่อตั้งค่าเป็นไปป์ไลน์ API Gateway

วัตถุส่งคืนของคุณจะต้องคล้ายกับตัวอย่างด้านล่าง:

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

มีข้อบกพร่องเล็กน้อยเนื่องจากต้องระมัดระวังเป็นพิเศษเกี่ยวกับการจัดการข้อผิดพลาดและการเชื่อมต่อฟังก์ชันแลมบ์ดาของคุณกับปลายทาง API เกตเวย์ ที่กล่าวว่าหากคุณไม่ได้ไปใช้ที่อื่นจริงๆก็ไม่ใช่ปัญหามากนัก


6

สำหรับผู้ที่ลองทำทุกอย่างในคำถามนี้และไม่สามารถทำงานนี้ได้ (เช่นฉัน) ให้ตรวจสอบความคิดเห็นของ thedevkit ในโพสต์นี้ (บันทึกวันของฉัน):

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

ทำซ้ำทั้งหมดด้านล่าง:

ฉันเคยมีปัญหากับตัวเองและฉันเชื่อว่าตัวละครขึ้นบรรทัดใหม่เป็นตัวการ

foo. * จะจับคู่เหตุการณ์ที่เกิดขึ้นของ "foo" ตามด้วยอักขระใด ๆ ยกเว้นขึ้นบรรทัดใหม่ โดยทั่วไปจะแก้ไขได้โดยการเพิ่มแฟล็ก '/ s' นั่นคือ "foo. * / s" แต่ regex ข้อผิดพลาดแลมบ์ดาดูเหมือนจะไม่เคารพสิ่งนี้

คุณสามารถใช้สิ่งอื่น ๆ เช่น foo (. | \ n) *


พบที่น่าอัศจรรย์! มันช่วยฉันได้หลายชั่วโมงในการกระแทกหัว! และยังห่างไกลจากความชัดเจน
Mirko Vukušić

Mirko ฉันดีใจที่ช่วยคุณได้!
Carlos Ballock

2

นี่คือวิธีที่แนะนำใน AWS Compute Blog หากใช้ API Gateway ตรวจสอบเพื่อดูว่าการผสานรวมใช้งานได้กับการเรียกใช้ Lambda โดยตรงหรือไม่

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

สำหรับการเรียกใช้ Lambda โดยตรงสิ่งนี้ดูเหมือนจะเป็นโซลูชันที่ดีที่สุดในการแยกวิเคราะห์ทางฝั่งไคลเอ็นต์


จะเกิดอะไรขึ้นถ้าตัวอย่างคือ lambda to lambda call นี่ยังคงเป็นสิ่งที่แลมด้าเรียกว่าจะกลับมาหรือไม่? และฉันจะอ่าน httpStatus นั้นบนแลมด้าที่เรียกได้อย่างไร
Rod

1

ฉันใช้เซิร์ฟเวอร์ 0.5 นี่คือวิธีการทำงานสำหรับกรณีของฉัน

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

หากคุณไม่ต้องการใช้พร็อกซีคุณสามารถใช้เทมเพลตนี้:

#set($context.responseOverride.status =  $input.path('$.statusCode'))
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.