Apakah ada cara untuk mengubah kode status http yang dikembalikan oleh Amazon API Gateway?

97

Misalnya jika saya ingin mengembalikan kesalahan 400 tertentu untuk parameter yang tidak valid atau mungkin 201 ketika panggilan fungsi lambda menghasilkan buat.

Saya ingin memiliki kode status http yang berbeda tetapi sepertinya api gateway selalu mengembalikan kode status 200 bahkan jika fungsi lambda mengembalikan kesalahan.

MonkeyBonkey
sumber
2
jadi sepertinya masalah yang saya alami adalah saya mengembalikan jenis kesalahan khusus - yang membuat regex errorMessage tidak berfungsi dengan benar. Mengembalikan string standar dalam respons gagal dari lambda akan membuat solusi di bawah ini berfungsi - namun, mengembalikan objek kesalahan khusus Anda sendiri tidak akan berhasil.
MonkeyBonkey
solusi saya adalah beralih dari Serveless versi 0.5 ke 1.0. Juga, saya menggunakan respons dari dokumentasi Serveless, menentukan statusCode di objek respons sebagai properti. Semoga membantu
Relu Mesaros

Jawaban:

79

Perbarui per 20-9-2016

Amazon akhirnya membuat ini mudah menggunakan integrasi Proxy Lambda . Ini memungkinkan fungsi Lambda Anda mengembalikan kode dan header HTTP yang tepat:

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

context.succeed(response);

Ucapkan selamat tinggal pada pemetaan permintaan / respons di API Gateway!

pilihan 2

Integrasikan aplikasi Express yang ada dengan Lambda / API Gateway menggunakan aws-serverless-express .

Eric Eijkelenboom
sumber
Saya tidak dapat mengintegrasikannya, maksud saya, saya mendapatkan status 200 dan respons yang dibuat (kesalahan yang dibuat). Apakah saya melewatkan sesuatu? Bagaimana tampilan "s-function.json"?
Relu Mesaros
Untuk contoh paling sederhana, lihat cetak biru Lambda AWS yang disebut microservice-http-endpoint (di konsol AWS Lambda). Anda menyebutkan "s-function.json", yang terdengar seperti Anda menggunakan framework Tanpa Server ( serverless.com ). Ini adalah binatang buas sepenuhnya dan jangan bingung dengan aws-serverless-express atau 'raw' Lambda / API Gateway. Jawaban saya tidak menjelaskan cara membuat ini berfungsi menggunakan kerangka kerja Tanpa Server.
Eric Eijkelenboom
7
Bagi siapa pun yang bertanya-tanya, ini juga bisa dicapai dengan menggunakan callbackgaya baru . Lakukan saja callback(null, {statusCode: 200, body: 'whatever'}).
Widdershin
1
@Sushil ya, Anda baru saja mengembalikan JSON seperti pada variabel respons di atas.
unclemeat
8
@Sushil Saya telah menyelesaikan ini dengan Python dengan LambdaProxyIntegration dan kembalireturn { "isBase64Encoded": True, "statusCode": 200, "headers": { }, "body": "" }
Jithu R Jacob
74

Berikut cara tercepat untuk mengembalikan Kode Status HTTP ubahsuaian dan ubahsuaian errorMessage:

Di dasbor API Gateway, lakukan hal berikut:

  1. Di metode untuk sumber daya Anda , klik respons metode
  2. Di tabel Status HTTP , klik tambahkan tanggapan dan tambahkan di setiap Kode Status HTTP yang ingin Anda gunakan.
  3. Di metode sumber daya Anda , klik respons integrasi
  4. Tambahkan respons integrasi untuk setiap Kode Status HTTP yang Anda buat sebelumnya. Pastikan input passthrough dicentang. Gunakan lambda error regex untuk mengidentifikasi kode status mana yang harus digunakan saat Anda mengembalikan pesan kesalahan dari fungsi lambda Anda. Sebagai contoh:

    // 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. Rute API Gateway Anda harus mengembalikan ini:

    HTTP Status Code: 400
    JSON Error Response: 
        {
            errorMessage: "Bad Request: You submitted invalid input"
        }
    
  6. Saya tidak melihat cara untuk menyalin pengaturan ini dan menggunakannya kembali untuk metode yang berbeda, jadi kami memiliki banyak input manual yang berlebihan yang harus dilakukan!

Tanggapan Integrasi saya terlihat seperti ini:

aws api gateway penanganan respons kesalahan lambda

ac360.dll
sumber
3
jadi sepertinya masalah saya adalah pemicu regex tidak pernah berfungsi karena saya mengembalikan objek kesalahan dari lambda dalam metode gagal, bukan hanya string. misalnyareturn context.fail(new Error('bad one'))
MonkeyBonkey
7
@kalisjoshua Saya baru-baru ini menerbitkan posting yang cukup rinci tentang penanganan kesalahan dengan API Gateway / Lambda: jayway.com/2015/11/07/…
Carl
9
Apa yang setara dengan context.fail untuk Python Lambda?
Routeburn
1
Untuk python: buat Exception. Lihat docs.aws.amazon.com/lambda/latest/dg/python-exceptions.html
devxoul
1
Apakah tidak ada cara untuk mengubah kode status dalam tanggapan non-kesalahan? Bagaimana jika saya ingin mengirim "201 Dibuat" bersama dengan objek yang dibuat?
Ben Davis
18

Untuk dapat mengembalikan objek kesalahan khusus sebagai JSON, Anda harus melewati beberapa rintangan.

Pertama, Anda harus gagal pada Lambda dan meneruskannya ke objek JSON yang dirangkai:

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

Selanjutnya, Anda menyiapkan pemetaan regex untuk setiap kode status yang ingin Anda kembalikan. Menggunakan objek yang saya definisikan di atas, Anda akan menyiapkan regex ini untuk 400:

. * "status": 400. *

Terakhir, Anda menyiapkan Template Pemetaan untuk mengekstrak respons JSON dari properti errorMessage yang dikembalikan oleh Lambda. Template Pemetaan terlihat seperti ini:

$ input.path ('$. errorMessage')

Saya menulis artikel tentang ini yang menjelaskan lebih detail dan menjelaskan aliran respons dari Lambda ke API Gateway di sini: http://kennbrodhagen.net/2016/03/09/how-to-return-a-custom-error-object -dan-status-kode-dari-api-gateway-dengan-lambda /

kennbrodhagen.dll
sumber
@kennbrodhagen Tahukah Anda tentang API Gateway dan Java Lambdas? Saya menggunakan sejenis reg exp yang sama, dan itu tidak bekerja untuk saya. Saya menggunakan. * StatusCode ": 422. *
Perimosh
@Perimosh lihat artikel ini yang menjelaskan cara melakukan ini dengan Pengecualian Java: aws.amazon.com/blogs/compute/…
kennbrodhagen
10

1) Konfigurasi sumber daya Gateway API Anda untuk menggunakan Integrasi Proxy Lambda dengan mencentang kotak centang berlabel "Gunakan integrasi Proksi Lambda" pada layar "Permintaan Integrasi" dari definisi sumber daya Gateway API. (Atau tentukan di konfigurasi cloudformation / terraform / serverless / etc Anda)

2) Ubah kode lambda Anda dengan 2 cara

  • Memproses masuk event(argumen fungsi ke-1) dengan tepat. Ini bukan lagi hanya payload kosong, ini mewakili seluruh permintaan HTTP termasuk header, string kueri, dan isi. Contoh di bawah. Poin utamanya adalah bahwa badan JSON akan menjadi string yang membutuhkan JSON.parse(event.body)panggilan eksplisit (jangan lupa try/catchtentang itu). Contohnya ada di bawah.
  • Merespon dengan memanggil panggilan balik dengan nol maka objek respon yang menyediakan rincian HTTP termasuk statusCode, body, dan headers.
    • bodyharus berupa string, jadi lakukan JSON.stringify(payload)sesuai kebutuhan
    • statusCode bisa berupa angka
    • headers adalah objek dari nama header ke nilai

Contoh Argumen Peristiwa Lambda untuk Integrasi 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
}

Contoh Bentuk Respons Panggilan Balik

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

Catatan - Saya yakin metode contextseperti context.succeed()itu sudah tidak digunakan lagi. Mereka tidak lagi didokumentasikan meskipun tampaknya masih berfungsi. Saya pikir pengkodean ke API panggilan balik adalah hal yang benar untuk selanjutnya.

Peter Lyons
sumber
Ini tidak bekerja. Saya masih mendapatkan 200 status dikembalikan dengan seluruh keluaran tanggapan ini. Tidak dapat mengatur api untuk benar-benar mengembalikan status 409
Andy N
7

Saya ingin kesalahan dari Lambda menjadi kesalahan 500 yang tepat, setelah melakukan banyak penelitian, muncul di bawah ini, yang berfungsi:

Di LAMBDA

Untuk tanggapan yang baik, saya kembali seperti di bawah ini:

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

Untuk respon yang buruk, kembali seperti di bawah ini

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

Di API Gateway

Untuk METODE DAPATKAN, ucapkan GET dari / res1 / service1:

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

Kemudian,

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

Sekarang, terbitkan / res1 / service1, tekan URL yang diterbitkan, yang terhubung ke lambda di atas

Menggunakan plugin chrome klien REST Lanjutan (atau Postman), Anda akan melihat kode http yang sesuai seperti kesalahan server (500) atau 400, alih-alih 200 kode tanggapan http untuk semua permintaan yang diberikan dalam "httpStatusCode".

Dari 'Dashboard' API, di API Gateway, kita bisa melihat kode status http seperti di bawah ini:

400 & 500 kesalahan

Manohar Reddy Poreddy
sumber
7

Cara termudah untuk melakukannya adalah dengan menggunakan integrasi LAMBDA_PROXY . Dengan metode ini, Anda tidak memerlukan transformasi khusus untuk disetel ke pipeline API Gateway.

Objek pengembalian Anda harus serupa dengan cuplikan di bawah ini:

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

Itu memang memiliki beberapa kekurangan: karena harus berhati-hati secara khusus tentang penanganan kesalahan, dan menggabungkan fungsi lambda Anda ke titik akhir API Gateway; yang mengatakan, jika Anda tidak benar-benar akan menggunakannya di tempat lain, itu tidak terlalu menjadi masalah.

Ricardo Nolde
sumber
6

Bagi mereka yang mencoba segalanya, tanyakan pertanyaan ini dan tidak dapat membuat ini berfungsi (seperti saya), periksa komentar thedevkit di posting ini (menyelamatkan hari saya):

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

Mereproduksi seluruhnya di bawah ini:

Saya sendiri memiliki masalah dengan ini, dan saya percaya bahwa karakter baris baru adalah pelakunya.

foo. * akan cocok dengan kemunculan "foo" diikuti dengan karakter apapun KECUALI baris baru. Biasanya ini diselesaikan dengan menambahkan tanda '/ s', yaitu "foo. * / S", tetapi ekspresi reguler kesalahan Lambda tampaknya tidak menghormati ini.

Sebagai alternatif, Anda dapat menggunakan sesuatu seperti: foo (. | \ N) *

Carlos Ballock
sumber
penemuan luar biasa! Itu hanya menyelamatkan saya berjam-jam membenturkan kepala! Dan itu jauh dari jelas.
Mirko Vukušić
Mirko, saya senang ini membantu Anda!
Carlos Ballock
2

Ini adalah cara yang direkomendasikan pada Blog Hitung AWS jika menggunakan API Gateway. Memeriksa untuk melihat apakah integrasi berfungsi dengan pemanggilan Lambda langsung.

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

Untuk pemanggilan Lambda langsung, ini tampaknya menjadi penguraian solusi terbaik di sisi klien.

spakmad
sumber
bagaimana jika contohnya adalah panggilan lambda ke lambda. apakah ini masih apa yang disebut lambda akan kembali? dan bagaimana saya bisa membaca httpStatus pada lambda pemanggil.
Batang
1

Saya menggunakan 0,5 tanpa server. Beginilah cara kerjanya, untuk kasus saya

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
sumber
1

Jika Anda tidak ingin menggunakan proxy, Anda dapat menggunakan template ini:

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