Есть ли способ изменить коды состояния http, возвращаемые Amazon API Gateway?

97

Например, если я хочу вернуть конкретную ошибку 400 для недопустимых параметров или, возможно, 201, когда вызов лямбда-функции привел к созданию.

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

ОбезьянаОбезьяна
источник
2
так что похоже, что проблема, с которой я столкнулся, заключалась в том, что я возвращал настраиваемый тип ошибки, что заставляет регулярное выражение errorMessage работать некорректно. При возврате стандартной строки в ответ об ошибке из лямбда-выражения приведенное ниже решение будет работать, однако возврат вашего собственного объекта ошибки не будет.
MonkeyBonkey
Мое решение состояло в том, чтобы перейти с Serveless версии 0.5 на 1.0. Кроме того, я использую ответ из документации Serveless, указав statusCode в объекте ответа как свойство. Надеюсь, это поможет
Релу Месарос

Ответы:

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 Gateway!

Вариант 2

Интегрируйте существующее приложение Express с Lambda / API Gateway с помощью aws-serverless-express .

Эрик Эйкеленбум
источник
1
Я не могу интегрировать это, то есть получаю статус 200 и созданный ответ (созданная ошибка). Я что-то упускаю ? Как выглядит "s-function.json"?
Релу Месарос,
7
Для всех, кто задается вопросом, этого также можно достичь с помощью нового callbackстиля. Просто сделай callback(null, {statusCode: 200, body: 'whatever'}).
Widdershin
1
@unclemeat У меня такие же вопросы. Вы в этом разобрались? Как это сделать на питоне?
Sushil
1
@Sushil да, вы просто возвращаете JSON, как в переменной ответа выше.
unclemeat 07
8
@Sushil Я решил это на Python с помощью LambdaProxyIntegration и вернулсяreturn { "isBase64Encoded": True, "statusCode": 200, "headers": { }, "body": "" }
Джиту Р Джейкоб
74

Вот самый быстрый способ вернуть пользовательские коды состояния HTTP и пользовательские errorMessage:

На панели управления API Gateway сделайте следующее:

  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. Я не вижу возможности скопировать эти настройки и повторно использовать их для разных методов, поэтому нам приходится делать много раздражающего избыточного ручного ввода!

Мои ответы интеграции выглядят так:

Обработка ответа лямбда-ошибки шлюза api aws

ac360
источник
3
поэтому похоже, что моя проблема заключалась в том, что триггер регулярного выражения никогда не работал, так как я возвращаю объект ошибки из лямбда-выражения в методе ошибки, а не просто строку. напримерreturn context.fail(new Error('bad one'))
MonkeyBonkey
7
@kalisjoshua Недавно я опубликовал довольно подробный пост об обработке ошибок с помощью API Gateway / Lambda: jayway.com/2015/11/07/…
Карл
9
Что эквивалентно context.fail для Python Lambda?
routeburn
1
Для python: вызовите исключение. См. Docs.aws.amazon.com/lambda/latest/dg/python-exceptions.html
devxoul,
1
Нет ли возможности изменить код состояния в ответах без ошибок? Что, если я хочу отправить «201 Created» вместе с созданным объектом?
Бен Дэвис
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));
};

Затем вы настраиваете отображение регулярных выражений для каждого из кодов состояния, которые вы хотите вернуть. Используя объект, который я определил выше, вы должны установить это регулярное выражение для 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-с-лямбда /

Kennbrodhagen
источник
@kennbrodhagen знаете ли вы об API Gateway и Java Lambdas? Я использую что-то вроде того же reg exp, и он у меня не работает. Я использую. * StatusCode ": 422. *
Perimosh
@Perimosh, прочтите эту статью, в которой объясняется, как это сделать с помощью исключений Java: aws.amazon.com/blogs/compute/…
kennbrodhagen
10

1) Настройте ресурс шлюза API для использования интеграции с лямбда-прокси , установив флажок «Использовать интеграцию с лямбда-прокси» на экране «Запрос интеграции» определения ресурса шлюза API. (Или определите его в конфигурации cloudformation / terraform / serverless / etc)

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 обратного вызова будет правильным в будущем.

Питер Лайонс
источник
1
Это не работает. Я все еще получаю 200 статусов, возвращаемых с этим полным ответом. Невозможно настроить API для фактического возврата статуса 409
Энди Н.
7

Я хотел, чтобы ошибка Lambda была правильной ошибкой 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-шлюзе

Для МЕТОДА ПОЛУЧЕНИЯ скажите 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')  

Теперь, publish / res1 / service1, нажмите опубликованный URL, который связан с вышеуказанной лямбда

Используя расширенный клиент REST (или почтальон) хром-плагин, вы увидите правильные HTTP-коды, такие как ошибка сервера (500) или 400, вместо 200 HTTP-кода ответа для всех запросов, которые были указаны в «httpStatusCode».

На панели управления API в API Gateway мы можем видеть коды статуса 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», но регулярное выражение лямбда-ошибок, похоже, не учитывает это.

В качестве альтернативы вы можете использовать что-то вроде: foo (. | \ N) *

Карлос Баллок
источник
потрясающая находка! Это просто сэкономило мне часы ударов головой! И это далеко не очевидно.
Мирко Вукушич
Мирко, я рад, что тебе помогло!
Карлос Баллок,
2

Вот как это рекомендуется в блоге AWS Compute при использовании API Gateway. Проверка, работает ли интеграция с прямым вызовом лямбда.

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

Для прямых вызовов Lambda это, по-видимому, лучшее решение для синтаксического анализа на стороне клиента.

Spakmad
источник
что, если бы пример был лямбда-вызовом лямбда-выражения. это все еще то, что вернула бы вызванная лямбда? и как я могу прочитать этот 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'))
Джордж Огден
источник