Есть ли способ изменить коды состояния http, возвращаемые Amazon API Gateway?
Например, если я хочу вернуть конкретную ошибку 400 для недопустимых параметров или, возможно, 201, когда вызов лямбда-функции привел к созданию.
Я хотел бы иметь разные коды состояния http, но похоже, что шлюз api всегда возвращает код состояния 200, даже если лямбда-функция возвращает ошибку.
12 ответов
Обновление за 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, используя https://github.com/awslabs/aws-serverless-express.
Вот самый быстрый способ вернуть пользовательские коды состояния HTTP и пользовательские errorMessage
:
На информационной панели шлюза API сделайте следующее:
- В методе для вашего ресурса, нажмите на метод ответа
- В таблице " Статус HTTP" нажмите " Добавить ответ" и добавьте каждый код состояния HTTP, который вы хотите использовать.
- В методе для вашего ресурса, нажмите на ответ интеграции
Добавьте интеграционный ответ для каждого из кодов состояния 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: .*
Ваш маршрут к API-шлюзу должен возвращать это:
HTTP Status Code: 400 JSON Error Response: { errorMessage: "Bad Request: You submitted invalid input" }
Я не вижу способа скопировать эти настройки и повторно использовать их для различных методов, поэтому у нас есть много раздражающих избыточных ручных вводов!
Мои интеграционные ответы выглядят так:
Чтобы иметь возможность возвращать пользовательский объект ошибки как 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 здесь: http://kennbrodhagen.net/2016/03/09/how-to-return-a-custom-error-object-and-status-code-from-api-gateway-with-lambda/
1) Настройте ресурс API-шлюза для использования интеграции Lambda Proxy, установив флажок "Использовать интеграцию Lambda Proxy" на экране "Запрос на интеграцию" определения ресурса 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 обратного вызова - это правильная вещь в будущем.
Я хотел, чтобы ошибка от 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 произнесите GET из /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, который связан с лямбдой выше
Использовав плагин Advanced REST client (или Postman) chrome, вы увидите правильные http-коды, такие как ошибка сервера (500) или 400, вместо 200 http-кода ответа для всех запросов, которые были заданы в "httpStatusCode".
На панели инструментов API в API Gateway мы можем видеть коды состояния http, как показано ниже:
Самый простой способ сделать это - использовать интеграцию 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; Тем не менее, если вы не собираетесь использовать его в другом месте, это не так уж много проблем.
Для тех, кто попробовал все, задавали этот вопрос и не мог сделать эту работу (как я), проверьте комментарий thedevkit к этому сообщению (спас мой день):
https://forums.aws.amazon.com/thread.jspa?threadID=192918
Воспроизведение полностью ниже:
У меня были проблемы с этим сам, и я считаю, что символы новой строки являются виновником.
foo.* будет соответствовать появлению "foo", за которым следуют любые символы, КРОМЕ новой строки. Обычно это решается добавлением флага '/ s', то есть "foo.* / S", но регулярное выражение лямбда-ошибки, похоже, не учитывает это.
В качестве альтернативы вы можете использовать что-то вроде: foo(.|\ N)*
Если вы не хотите использовать прокси, вы можете использовать этот шаблон:
#set($context.responseOverride.status = $input.path('$.statusCode'))
Именно так рекомендуется в блоге AWS Compute Blog при использовании 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-вызовов это, кажется, лучшее решение для анализа на стороне клиента.
У меня есть экспресс-приложение, и я использовал API-шлюз перед ним с интеграцией http. Чтобы вернуть код состояния из моего приложения вместо 200 OK , я просто добавил код состояния http, возвращаемый обработчиком ошибок моего приложения, в регулярное выражение состояния http в разделе ответа интеграции, и он отлично работает. Убедитесь, что ваше приложение правильно обрабатывает ошибку.
Действительно с февраля 2021 г.
Самый простой способ установить собственный код состояния HTTP - настроить интеграцию прокси-сервера Lambda в API-шлюз.
В API Gateway> Ресурс> Раскрывающийся список действий> Создать метод> отметьте
Lambda Proxy Integration
и выберите соответствующую лямбда-функцию.
Лямбда
Для асинхронных функций просто возвращайтесь с объектом с
statusCode
и
body
. Для функции синхронизации используйте
callback(null,obj)
; обратитесь к полной документации .
export const dummyFunction = async (event, context, callback) =>
{
// ... logic
return {
statusCode: 400,
body: JSON.stringify({...data}),
}
};
Результат
Пользовательский код статуса 400.
Я использую без сервера 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;