Автор оригинала: Markus Gulden.
1. Обзор
AWS Ламбда это служба без серверов, предоставляемая Amazon Web Services.
В двух предыдущих статьях мы обсуждали, как создать функцию AWS Lambda с помощью Java, а также как получить доступ к DynamoDB из функции Lambda.
В этом учебнике мы обсудим как опубликовать функцию Lambda в качестве конечной точки REST, используя AWS Шлюз .
Мы подробно посмотрим на следующие темы:
- Основные концепции и условия API Gateway
- Интеграция функций Lambda с API Gateway с помощью интеграции Lambda Proxy
- Создание API, его структура и способы картировать ресурсы API на функции Lambda
- Развертывание и тестирование API
2. Основы и условия
API Gateway является полностью управляемый сервис, позволяющий разработчикам создавать, публиковать, поддерживать, контролировать и защищивать API в любом масштабе .
Мы можем внедрить последовательный и масштабируемый интерфейс программирования на основе HTTP (также называемый RESTful services) доступ к бэкэнд-сервисам, таким как функции Lambda, дополнительные услуги AWS (например, EC2, S3, DynamoDB) и любые конечные точки HTTP .
Функции включают, но не ограничиваются:
- Управление дорожным движением
- Авторизация и контроль доступа
- контроль
- Управление версиями API
- Запросы на регулирование для предотвращения атак
Как и AWS Lambda, API Gateway автоматически масштабируется и выставлен счет за вызов API.
Подробную информацию можно найти в официальная документация .
2.1. Условия
API Шлюз это служба AWS, которая поддерживает создание, развертывание и управление интерфейсом программирования приложений RESTful для предоставления бэкэнда конечных точек HTTP, функций AWS Lambda и других служб AWS.
API шлюз API представляет собой набор ресурсов и методов, которые могут быть интегрированы с функциями Lambda, другими службами AWS или конечными точками HTTP в бэкэнде. API состоит из ресурсов, формя которые формируют структуру API. Каждый ресурс API может предоставить один или несколько методов API, которые должны иметь уникальные глаголы HTTP.
Чтобы опубликовать API, мы должны создать Развертывание API и связать его с так называемой этап . Этап, как снимок во время API. Если мы передислоцируем API, мы можем либо обновить существующий этап, либо создать новый. Таким образом, возможны одновременно различные версии API, например dev этап, тестовый этапе, и даже несколько производственных версий, как v1 , v2 и так далее.
Услуги по интеграции По доверенности Lambda — упрощенная конфигурация для интеграции функций Lambda и API Gateway.
Шлюз API отправляет весь запрос в качестве ввода в функцию бэкэнд Lambda. В ответ на запрос API Gateway преобразует выход функции Lambda обратно в интерфейс ответа HTTP.
3. Зависимости
Нам нужны те же зависимости, что и в AWS Lambda, используя DynamoDB со статьей Java.
Кроме того, нам также нужна JSON Простые библиотека:
com.googlecode.json-simple json-simple 1.1.1
4. Разработка и развертывание функций Lambda
В этом разделе мы разработаем и построим наши функции Lambda на Java, развернем их с помощью консоли AWS и заведем быстрый тест.
Поскольку мы хотим продемонстрировать основные возможности интеграции API Gateway с Lambda, мы создадим две функции:
- Функция 1: получает полезную нагрузку от API с помощью метода PUT
- Функция 2: демонстрирует, как использовать параметр пути HTTP или параметр запроса HTTP, поступающий от API
Реализация мудрый, мы создадим один ЗапросХэндлер класс, который имеет два метода – по одному для каждой функции.
4.1. Модель
Прежде чем реализовать обработчик фактических запросов, давайте кратко рассмотрим нашу модель данных:
public class Person { private int id; private String name; public Person(String json) { Gson gson = new Gson(); Person request = gson.fromJson(json, Person.class); this.id = request.getId(); this.name = request.getName(); } public String toString() { Gson gson = new GsonBuilder().setPrettyPrinting().create(); return gson.toJson(this); } // getters and setters }
Наша модель состоит из одного простого Лицо класса, который имеет два свойства. Единственной примечательной частью является Человек (Струна) конструктор, который принимает строку JSON.
4.2. Осуществление класса RequestHandler
Так же, как в AWS Lambda с Java статьи, мы создадим реализацию ЗапросСтримХандлер интерфейс:
public class APIDemoHandler implements RequestStreamHandler { private static final String DYNAMODB_TABLE_NAME = System.getenv("TABLE_NAME"); @Override public void handleRequest( InputStream inputStream, OutputStream outputStream, Context context) throws IOException { // implementation } public void handleGetByParam( InputStream inputStream, OutputStream outputStream, Context context) throws IOException { // implementation } }
Как видим, ЗапросСтримХайдер интерфейс определяет только один метод, handeRequest() . Во всяком случае, мы можем определить дальнейшие функции в том же классе, как мы сделали здесь. Другой вариант заключается в создании одной реализации ЗапросСтримХайдер для каждой функции.
В нашем конкретном случае, мы выбрали первый для простоты. Однако выбор должен быть сделан на каждом конкретном случае с учетом таких факторов, как производительность и устойчивость кода.
Мы также читаем название нашей таблицы DynamoDB из TABLE_NAME переменная среды. Мы определим эту переменную позже во время развертывания.
4.3. Осуществление функции 1
В нашей первой функции мы хотим продемонстрировать как получить полезную нагрузку (например, от запроса PUT или POST) из api Gateway :
public void handleRequest( InputStream inputStream, OutputStream outputStream, Context context) throws IOException { JSONParser parser = new JSONParser(); BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream)); JSONObject responseJson = new JSONObject(); AmazonDynamoDB client = AmazonDynamoDBClientBuilder.defaultClient(); DynamoDB dynamoDb = new DynamoDB(client); try { JSONObject event = (JSONObject) parser.parse(reader); if (event.get("body") != null) { Person person = new Person((String) event.get("body")); dynamoDb.getTable(DYNAMODB_TABLE_NAME) .putItem(new PutItemSpec().withItem(new Item().withNumber("id", person.getId()) .withString("name", person.getName()))); } JSONObject responseBody = new JSONObject(); responseBody.put("message", "New item created"); JSONObject headerJson = new JSONObject(); headerJson.put("x-custom-header", "my custom header value"); responseJson.put("statusCode", 200); responseJson.put("headers", headerJson); responseJson.put("body", responseBody.toString()); } catch (ParseException pex) { responseJson.put("statusCode", 400); responseJson.put("exception", pex); } OutputStreamWriter writer = new OutputStreamWriter(outputStream, "UTF-8"); writer.write(responseJson.toString()); writer.close(); }
Как уже говорилось ранее, мы настроили API позже, чтобы использовать интеграцию прокси-серверов Lambda. Мы ожидаем, что ШЛЮЗ API передат полный запрос функции Lambda в Входной поток параметр.
Все, что нам нужно сделать, это выбрать соответствующие атрибуты из содержащейся структуры JSON.
Как мы видим, метод в основном состоит из трех этапов:
- Получение тело объект из нашего входного потока и создания Лицо объект из этого
- Хранение этого Лицо объект в таблице DynamoDB
- Создание объекта JSON, который может в держать несколько атрибутов, как тело для ответа, пользовательские заготовки, а также код статуса HTTP
Один момент стоит упомянуть здесь: API Gateway ожидает тело быть Струнные (как для запроса, так и для ответа).
Как мы ожидаем получить Струнные как тело от API Gateway, мы бросили тело Струнные и инициализировать нашу Лицо объект:
Person person = new Person((String) event.get("body"));
API Gateway также ожидает ответа тело быть Струнные :
responseJson.put("body", responseBody.toString());
Эта тема прямо не упоминается в официальной документации. Однако, если мы присмотримся, мы увидим, что атрибут тела является Струнные в обоих фрагментах для запроса а также для ответных .
Преимущество должно быть ясным: даже если JSON является форматом между API Gateway и функцией Lambda, фактический орган может содержать простой текст, JSON, XML, или любой другой. Именно тогда функция Lambda должна правильно обрабатывать формат.
Мы увидим, как выглядит тело запроса и ответа позже, когда мы тестируем наши функции в консоли AWS.
То же самое относится и к следующим двум функциям.
4.4. Осуществление функции 2
На втором этапе мы хотим продемонстрировать как использовать параметр пути или параметр строки запроса для извлечения Лицо элемент из базы данных с использованием идентификатора:
public void handleGetByParam( InputStream inputStream, OutputStream outputStream, Context context) throws IOException { JSONParser parser = new JSONParser(); BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream)); JSONObject responseJson = new JSONObject(); AmazonDynamoDB client = AmazonDynamoDBClientBuilder.defaultClient(); DynamoDB dynamoDb = new DynamoDB(client); Item result = null; try { JSONObject event = (JSONObject) parser.parse(reader); JSONObject responseBody = new JSONObject(); if (event.get("pathParameters") != null) { JSONObject pps = (JSONObject) event.get("pathParameters"); if (pps.get("id") != null) { int id = Integer.parseInt((String) pps.get("id")); result = dynamoDb.getTable(DYNAMODB_TABLE_NAME).getItem("id", id); } } else if (event.get("queryStringParameters") != null) { JSONObject qps = (JSONObject) event.get("queryStringParameters"); if (qps.get("id") != null) { int id = Integer.parseInt((String) qps.get("id")); result = dynamoDb.getTable(DYNAMODB_TABLE_NAME) .getItem("id", id); } } if (result != null) { Person person = new Person(result.toJSON()); responseBody.put("Person", person); responseJson.put("statusCode", 200); } else { responseBody.put("message", "No item found"); responseJson.put("statusCode", 404); } JSONObject headerJson = new JSONObject(); headerJson.put("x-custom-header", "my custom header value"); responseJson.put("headers", headerJson); responseJson.put("body", responseBody.toString()); } catch (ParseException pex) { responseJson.put("statusCode", 400); responseJson.put("exception", pex); } OutputStreamWriter writer = new OutputStreamWriter(outputStream, "UTF-8"); writer.write(responseJson.toString()); writer.close(); }
Опять же, три шага актуальны:
- Мы проверяем, есть ли путьПараметеры или queryStringParameters массив с id атрибут присутствуют.
- Если истинное , мы используем стоимость принадлежности, чтобы запросить Лицо элемент с этим идентификатором из базы данных.
- Мы добавляем представление JSON полученного пункта к ответу.
В официальной документации содержится более подробное объяснение входный формат и выходной формат для интеграции прокси.
4.5. Строительный кодекс
Опять же, мы можем просто построить наш код с помощью Maven:
mvn clean package shade:shade
Файл JAR будет создан под целевые папка.
4.6. Создание таблицы ДинамоДБ
Мы можем создать таблицу, как объяснили в AWS Lambda Использование DynamoDB с Java .
Давайте выберем Лицо как название таблицы, id в качестве основного ключевого имени и Номер в качестве основного ключа.
4.7. Развертывание кода через консоль AWS
После создания нашего кода и создания таблицы мы можем создавать функции и загружать код.
Это можно сделать, повторив шаги 1-5 из AWS Lambda со статьей Java, по одному разу для каждого из наших двух методов.
Давайте использовать следующие имена функций:
- МагазинПерсонФункция для ручкаРеквест метод (функция 1)
- GetPersonByHTTPParamFunction для handleGetByParam метод (функция 2)
Мы также должны определить переменную среду TABLE_NAME с значением “Человек” .
4.8. Тестирование функций
Прежде чем продолжить работу с фактической частью API Gateway, мы можем продать быстрый тест в консоли AWS, просто чтобы проверить, правильно ли работают наши функции Lambda и что мы можем справиться с форматом интеграции прокси.
Тестирование функции Lambda на консоли AWS работает, как описано в статье AWS Lambda с Java.
Тем не менее, когда мы создаем тест-событие, мы должны рассмотреть специальный формат интеграции прокси , которые наши функции ожидают. Мы можем использовать API шлюз AWS прокси шаблон и настроить, что для наших нужд, или мы можем скопировать и вставить следующие события:
Для МагазинПерсонФункция , мы должны использовать это:
{ "body": "{\"id\": 1, \"name\": \"John Doe\"}" }
Как уже говорилось ранее, тело должны иметь тип Струнные , даже если содержит структуру JSON. Причина в том, что API Gateway будет отправлять свои запросы в том же формате.
Следующий ответ должен быть возвращен:
{ "isBase64Encoded": false, "headers": { "x-custom-header": "my custom header value" }, "body": "{\"message\":\"New item created\"}", "statusCode": 200 }
Здесь мы видим, что тело нашего ответа является Струнные , хотя он содержит структуру JSON.
Давайте посмотрим на вход для GetPersonByHTTPParamFunction.
Для тестирования функциональности параметра пути ввод будет выглядеть так:
{ "pathParameters": { "id": "1" } }
И вход для отправки параметра строки запроса будет:
{ "queryStringParameters": { "id": "1" } }
В качестве ответа мы должны получить следующее для обоих методов:
{ "headers": { "x-custom-header": "my custom header value" }, "body": "{\"Person\":{\n \"id\": 88,\n \"name\": \"John Doe\"\n}}", "statusCode": 200 }
Опять же, тело является Струнные .
5. Создание и тестирование API
После того, как мы создали и развернули функции Lambda в предыдущем разделе, теперь мы можем создать фактический API с помощью AWS Console .
Рассмотрим основной рабочий процесс:
- Создайте API в нашей учетной записи AWS.
- Добавьте ресурс в иерархию ресурсов API.
- Создайте один или несколько методов для ресурса.
- Настройка интеграции между методом и функцией Ламбды.
Мы повторим шаги 2-4 для каждой из наших двух функций в следующих разделах.
5.1. Создание API
Для создания API нам придется:
- Во войте на консоль API Gateway в https://console.aws.amazon.com/apigateway
- Нажмите на кнопку “Начать работу”, а затем выберите “Новый API”
- Ввемите имя нашего API ( ТестАПИ ) и признать, нажав на “Создать API”
Создав API, мы можем создать структуру API и связать ее с нашими функциями Lambda.
5.2. Структура API для функции 1
Для нашей жизни необходимы следующие МагазинПерсонФункция :
- Выберите элемент родительского ресурса под деревом “Ресурсы”, а затем выберите “Создать ресурс” из меню “Действия”. Затем, мы должны сделать следующее в “Новый детский ресурс” панели:
- Ввет “Лица” в качестве имени в поле ввода текста “Resource Name”
- Оставьте значение по умолчанию в поле ввода текста “Путь ресурсов”
- Выберите “Создать ресурс”
- Выберите только что созданный ресурс, выберите “Создать метод” из меню “Действия” и выполнить следующие шаги:
- Выберите PUT из списка высадки метода HTTP, а затем выберите значок чекового знака, чтобы сохранить выбор
- Оставьте “Lambda Function” в качестве интеграционного типа и выберите опцию “Использовать интеграцию Lambda Proxy”
- Выберите регион из “Региона Ламбда”, где мы развернули наши функции Lambda раньше
- Тип “StorePersonFunction” в “Функции Ламбды”
- Выберите “Сохранить” и признать с “ОК”, когда предложено с “Добавить разрешение на функцию Lambda”
5.3. Структура API для функции 2 – Параметры пути
Шаги для наших параметров пути извлечения аналогичны:
Выберите / лица элемент ресурса под деревом “Ресурсы”, а затем выберите “Создать ресурс” из меню “Действия”. Затем мы должны сделать следующее в панели New Child Resource:
- Тип “Человек” как имя в поле ввода текста “Resource Name”
- Измените поле ввода текста “Путь ресурсов” на (Мид)”
- Выберите “Создать ресурс”
- Выберите только что созданный ресурс, выберите “Создать метод” из меню “Действия” и выполнить следующие шаги:
- Выберите GET из списка высадки метода HTTP, а затем выберите значок чекового знака, чтобы сохранить выбор
- Оставьте “Lambda Function” в качестве интеграционного типа и выберите опцию “Использовать интеграцию Lambda Proxy”
- Выберите регион из “Региона Ламбда”, где мы развернули наши функции Lambda раньше
- Тип “GetPersonByHTTPParamFunction” в “Функции Ламбды”
- Выберите “Сохранить” и признать с “ОК”, когда предложено с “Добавить разрешение на функцию Lambda”
Примечание: здесь важно установить параметр «Ресурсный путь» для “Мид” , как наш GetPersonByPathParamFunction ожидает, что этот параметр будет назван именно так.
5.4. Структура API для функции 2 – Параметры строки запроса
Шаги для получения параметров строки запроса немного отличаются, как мы не должны создавать ресурс, но вместо этого должны создать параметр запроса для id параметр :
Выберите /лица элемент ресурса под деревом “Ресурсы” выберите “Создать метод” из меню “Действия” и выполнить следующие шаги:
- Выберите GET из списка высадки метода HTTP, а затем выберите значок галочки, чтобы сохранить выбор
- Оставьте “Lambda Function” в качестве интеграционного типа и выберите опцию “Использовать интеграцию Lambda Proxy”
- Выберите регион из “Региона Ламбда”, где мы развернули наши функции Lambda раньше
- Тип “GetPersonByHTTPParamFunction” в “Функции Ламбды”.
- Выберите “Сохранить” и признать с “ОК”, когда предложено с “Добавить разрешение на функцию Lambda”
- Выберите “Метод Запрос” справа и выполнить следующие шаги:
- Расширить список параметров строки URL-адреса
- Нажмите на строку “Добавить строку запроса”
- Тип “id” в поле имени и выберите значок чекового знака, чтобы сохранить
- Выберите “Обязательный” флажок
- Нажмите на символ пера рядом с “Запрос валидатора” в верхней части панели, выберите “Проверить параметры строки запроса и заготовки”, и выберите значок чекового знака
Примечание: Важно установить параметр “Строка запроса” для “id” , как наш GetPersonByHTTPParamFunction ожидает, что этот параметр будет назван именно так.
5.5. Тестирование API
Наш API уже готов, но пока не является общедоступом. Прежде чем опубликовать его, мы хотим запустить быстрый тест с первого .
Для этого мы можем выбрать соответствующий метод для тестирования в дереве «Ресурсы» и нажать на кнопку «Тест». На следующем экране, мы можем ввести в нашем входе, как мы хотели бы отправить его с клиентом через HTTP.
Для МагазинПерсонФункция , мы должны ввести следующую структуру в поле “Запрос тела”:
{ "id": 2, "name": "Jane Doe" }
Для GetPersonByHTTPParamFunction с параметрами пути, мы должны ввести 2 в качестве значения в поле «Идентификация» в разделе «Путь».
Для GetPersonByHTTPParamFunction с параметрами строки запроса, мы должны ввести id-2 в качестве значения в поле «лица» в разделе «Строки запроса».
5.6. Развертывание API
До сих пор наш API не был общедоступен и, таким образом, был доступен только с консоли AWS.
Как уже говорилось ранее, при развертывании API мы должны связать его со сценой, которая похожа на моментальный снимок во время API. Если мы перераспределим API, мы можем либо обновить существующий этап, либо создать новый .
Давайте посмотрим, как будет выглядеть схема URL для нашего API:
https://{restapi-id}.execute-api.{region}.amazonaws.com/{stageName}
Для развертывания необходимы следующие шаги:
- Выберите конкретный API в навигационном панели “API”
- Выберите “Действия” в навигационном панели Ресурсов и выберите “Развертывание API” из меню “Действия”
- Выберите “Новый этап” из “Стадии развертывания” падение вниз, тип “тест” в “Сценическое имя”, и дополнительно предоставить описание этапа и развертывания
- Запустите развертывание, выбрав “Развертывание”
После последнего шага консоль предоставит корневой URL API, например, https://0skaqfgdw4.execute-api.eu-central-1.amazonaws.com/test .
5.7. Вызов конечной точки
Поскольку API является общедоступной, мы можем назвать его с помощью любого клиента HTTP мы хотим .
С cURL , звонки будут выглядеть следующим образом.
МагазинПерсонФункция :
curl -X PUT 'https://0skaqfgdw4.execute-api.eu-central-1.amazonaws.com/test/persons' \ -H 'content-type: application/json' \ -d '{"id": 3, "name": "Richard Roe"}'
GetPersonByHTTPParamFunction для параметров пути:
curl -X GET 'https://0skaqfgdw4.execute-api.eu-central-1.amazonaws.com/test/persons/3' \ -H 'content-type: application/json'
GetPersonByHTTPParamFunction для параметров строки запроса:
curl -X GET 'https://0skaqfgdw4.execute-api.eu-central-1.amazonaws.com/test/persons?id=3' \ -H 'content-type: application/json'
6. Заключение
В этой статье мы посмотреть, как сделать функции AWS Lambda доступными в качестве конечных точек REST, используя AWS API Gateway.
Мы изучили основные концепции и терминологию API Gateway, и мы узнали, как интегрировать функции Lambda с помощью интеграции Прокси Lambda.
Наконец, мы увидели, как создать, развернуть и протестировать API.
Как обычно, весь код для этой статьи доступен в течение на GitHub .