Чи є спосіб змінити коди статусу http, повернені шлюзом API Amazon?


95

Наприклад, якщо я хочу повернути конкретну помилку 400 для недійсних параметрів або, можливо, 201, коли виклик лямбда-функції призвів до створення.

Я хотів би мати різні коди статусу http, але схоже, що шлюз api завжди повертає код 200 статусу, навіть якщо функція лямбда повертає помилку.


2
Таким чином, схоже, що у мене виникло питання про те, що я повертав користувальницький тип помилок - через що регулярний випадок errorMessage не працює належним чином. Повернення стандартного рядка у відповідь на помилку від лямбда змусить вказане нижче рішення працювати - повернення власного об'єкта власної помилки, однак, не буде.
MonkeyBonkey

моє рішення полягало в тому, щоб перейти з версії Serveless 0.5 на 1.0. Крім того, я використовую відповідь з безсервісної документації, вказуючи statusCode в об'єкті відповіді як властивість. Сподіваюся, це допоможе
Relu Mesaros

Відповіді:


79

Оновлення за 20-9-2016

Нарешті, Amazon зробив це легко, використовуючи інтеграцію Lambda Proxy . Це дозволяє вашій лямбда-функції повертати належні коди HTTP і заголовки:

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

context.succeed(response);

Попрощайтеся зі складанням запитів / відповідей у ​​шлюзі API!

Варіант 2

Інтегруйте існуючу програму Express із Lambda / API Gateway за допомогою aws-serverless-express .


Я не можу інтегрувати його, я маю на увазі, я отримую статус 200 і створену відповідь (створена помилка). Мені чогось не вистачає? Як виглядає "s-function.json"?
Релу Месарос

Для найпростішого прикладу подивіться власний креслення Lambda під назвою microservice-http-endpoint (на консолі AWS Lambda). Ви згадуєте "s-function.json", що звучить так, ніби ви використовуєте безсерверну структуру ( serverless.com ). Це ще один звір, і його не слід плутати з aws-serverless-express або `` необробленим '' шлюзом Lambda / API. Моя відповідь не описує, як зробити цю роботу за допомогою сервера без фреймворку.
Ерік Ейкеленбум

7
Для всіх, хто цікавиться, цього можна досягти і за допомогою нового callbackстилю. Просто зробіть callback(null, {statusCode: 200, body: 'whatever'}).
Віддершин

1
@Sushil так, ви просто повертаєте JSON, як у змінній відповіді вище.
нечисте м'ясо

8
@Sushil Я вирішив це в Python з LambdaProxyIntegration і повернувсяreturn { "isBase64Encoded": True, "statusCode": 200, "headers": { }, "body": "" }
Jithu R Jacob

74

Ось найшвидший спосіб повернути власні коди статусу HTTP та спеціальні errorMessage:

На інформаційній панелі шлюзу API виконайте такі дії:

  1. У методі для вашого ресурсу натисніть на відповідь методу
  2. У таблиці статусу HTTP натисніть Додати відповідь та додайте кожен код стану HTTP, який ви хочете використовувати.
  3. У методі для вашого ресурсу натисніть відповідь на інтеграцію
  4. Додайте відповідь на інтеграцію для кожного з кодів статусу HTTP, які ви створили раніше. Переконайтесь, що прапорець вводу встановлено. Використовуйте регулярний вираз лямбда-помилки, щоб визначити, який код стану слід використовувати, коли ви повертаєте повідомлення про помилку з вашої лямбда-функції. Наприклад:

    // 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 повинен повернути це:

    HTTP Status Code: 400
    JSON Error Response: 
        {
            errorMessage: "Bad Request: You submitted invalid input"
        }
    
  6. Я не бачу способу скопіювати ці налаштування та повторно використовувати їх для різних методів, тому нам потрібно зробити настільки надокучливе зайве введення вручну!

Мої інтеграційні відповіді виглядають приблизно так:

Aws api шлюз лямбда-обробки відповіді на помилки


3
отже, моєю проблемою було те, що тригер регулярного виразу ніколи не працював, оскільки я повертаю об'єкт помилки з лямбда-методу в метод відмови, а не просто рядок. наприкладreturn context.fail(new Error('bad one'))
MonkeyBonkey

7
@kalisjoshua Нещодавно я опублікував досить детальну публікацію про обробку помилок з API Gateway / Lambda: jayway.com/2015/11/07/…
Карл

9
Що таке еквівалент context.fail для Python Lambda?
маршрут спалити

1
Для python: підняти виняток. Дивіться docs.aws.amazon.com/lambda/latest/dg/python-exceptions.html
devxoul

1
Чи немає способу змінити код статусу у невідповідних відповідях? Що робити, якщо я хотів надіслати "201 Створено" разом із створеним об'єктом?
Бен Девіс

18

Щоб мати змогу повернути користувальницький об’єкт помилки як JSON, вам потрібно перескочити пару обручів.

По-перше, ви повинні відмовитись від лямбда та передати їй об'єктивований об'єкт 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));
};

Далі ви встановлюєте регулярне вирівнювання для кожного з кодів статусу, який ви хочете повернути. Використовуючи об'єкт, який я визначив вище, ви встановите цей регулярний вираз для 400:

. * "статус": 400. *

Нарешті, ви встановлюєте шаблон картографування для вилучення відповіді JSON з властивості errorMessage, поверненої Lambda. Шаблон відображення виглядає так:

$ input.path ('$. errorMessage')

Я написав статтю з цього приводу, яка детальніше і пояснює потік відповідей від Lambda до шлюзу API тут: 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 ознайомтесь із цією статтею, яка пояснює, як це зробити за винятком Java: aws.amazon.com/blogs/compute/…
kennbrodhagen

10

1) Налаштуйте свій ресурс шлюзу API для використання інтеграції проксі Lambda , встановивши прапорець "Використовувати інтеграцію проксі-лямбда" на екрані "Запит інтеграції" визначення ресурсу шлюзу API. (Або визначте це у своїй конфігурації хмари / тераформи / без сервера / тощо)

2) Змініть свій лямбда-код двома способами

  • Обробляйте вхідний event(аргумент 1-ї функції) належним чином. Це вже не просто гола корисна навантаження, вона представляє весь HTTP-запит, включаючи заголовки, рядок запиту та тіло. Зразок нижче. Ключовим моментом є те, що тіла JSON будуть рядками, що вимагають явного JSON.parse(event.body)виклику (не забувайте про try/catchце). Приклад наведено нижче.
  • Відповісти на виклик зворотного виклику з нульовим , тоді об'єктом відповіді , який забезпечує деталь HTTP , включаючи statusCode, bodyі headers.
    • bodyмає бути рядком, тому робіть JSON.stringify(payload)за потреби
    • statusCode може бути числом
    • headers є об'єктом назв заголовків до значень

Зразок аргументу події лямбда для інтеграції проксі

{
    "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 зворотного виклику - це правильна річ у майбутньому.


Це не працює. Я все ще отримую статус 200, повернутий із цілим результатом відповіді. Не вдається встановити API для фактичного повернення статусу 409
Енді Н,

7

Я хотів, щоб помилка від Lambda була правильною 500 помилка, після проведення багатьох досліджень, я придумав нижче, що працює:

На ЛАМБДА

Для гарної відповіді я повертаюсь, як показано нижче:

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

Щоб отримати МЕТОД, скажіть 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-адресу, підключену до лямбда

Використовуючи розширений клієнтський плагін REST (або поштар) chrome, ви побачите правильні коди http, такі як помилка сервера (500) або 400, а не 200 код відповіді http для всіх запитів, які були вказані в "httpStatusCode".

З "Інформаційної панелі" API в шлюзі API ми бачимо коди статусу http, як показано нижче:

Помилки 400 і 500


7

Найпростіший спосіб зробити це - використовувати інтеграцію LAMBDA_PROXY . Використовуючи цей метод, вам не потрібні спеціальні перетворення для встановлення в трубопровід API шлюзу.

Ваш об’єкт повернення повинен бути схожим на фрагмент нижче:

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", але регулярний вираз помилки Лямбда, схоже, не поважає цього.

В якості альтернативи ви можете використовувати щось на кшталт: foo (. | \ N) *


дивовижна знахідка! Це просто врятувало мені години удару головою! І це далеко не очевидно.
Мірко Вукушич

Мирко, я радий, що тобі це допомогло!
Карлос Баллок

2

Ось як це рекомендується в AWS Compute Blog, якщо використовується шлюз API. Перевірка, чи працює інтеграція із прямим викликом Лямбди.

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

Для прямих викликів лямбди це найкраще рішення для аналізу на стороні клієнта.


а якщо приклад - виклик лямбда-лямбда. це все-таки повернеться названа лямбда? і як я можу прочитати цей httpStatus на виклику лямбда.
Прут

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'))
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.