1. 概述
AWS Lambda 是亞馬遜雲服務提供的無服務器計算服務。
在之前的兩篇文章中,我們討論瞭如何使用 Java 創建 AWS Lambda 函數,以及如何從 Lambda 函數訪問 DynamoDB。
在本教程中,我們將討論 如何將 Lambda 函數發佈為 REST 端點,使用 AWS Gateway。
我們將深入探討以下主題:
- API Gateway 的基本概念和術語
- 使用 Lambda 代理集成將 Lambda 函數與 API Gateway 集成
- 創建 API、其結構以及如何將 API 資源映射到 Lambda 函數
- API 的部署和測試
。
我們可以實現一個一致且可擴展的基於 HTTP 的編程接口(也稱為 RESTful 服務)官方文檔 中找到。
是一個 AWS 服務,支持創建、部署和管理 RESTful 應用程序編程接口,以公開後端 HTTP 端點、AWS Lambda 函數和其他 AWS 服務。
一個 並將其與所謂的 stage 相關聯。一個階段就像 API 的時間快照。如果重新部署 API,我們可以更新現有階段或創建一個新階段。通過這樣,可以在同一時間對 API 的不同版本是可能的,例如 dev 階段、test 階段,甚至多個生產版本,如 v1、v2 等。
Lambda Proxy integration 是 Lambda 函數和 API Gateway 之間集成的簡易配置。
API Gateway 將整個請求作為輸入發送到後端 Lambda 函數。響應方面,API Gateway 將 Lambda 函數的輸出轉換為前端 HTTP 響應。
3. 依賴項
我們需要與在“使用Java的AWS Lambda與DynamoDB”文章中相同的依賴項。
除此之外,我們還需要JSON Simple庫:
<dependency>
<groupId>com.googlecode.json-simple</groupId>
<artifactId>json-simple</artifactId>
<version>1.1.1</version>
</dependency>
4. Developing and Deploying the Lambda Functions
In this section, we’ll develop and build our Lambda functions in Java, we’ll deploy it using AWS Console, and we’ll run a quick test.
As we want to demonstrate the basic capabilities of integrating API Gateway with Lambda, we’ll create two functions:
- Function 1: receives a payload from the API, using a PUT method
- Function 2: demonstrates how to use an HTTP path parameter or HTTP query parameter coming from the API
Implementation-wise, we’ll create one RequestHandler class, which has two methods— one for each function.
4.1. Model
Before we implement the actual request handler, let’s have a quick look at our data model:
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
}
Our model consists of one simple Person class, which has two properties. The only notable part is the Person(String) constructor, which accepts a JSON String.
4.2. Implementation of the RequestHandler Class
Just like in the AWS Lambda With Java article, we’ll create an implementation of the RequestStreamHandler interface:
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
}
}
As we can see, the RequestStreamHander interface defines only one method, handeRequest(). Anyhow, we can define further functions in the same class, as we’ve done here. Another option would be to create one implementation ofRequestStreamHander for each function.
In our specific case, we chose the former for simplicity. However, the choice must be made on a case-by-case basis, taking into consideration such factors as performance and code maintainability.
We also read the name of our DynamoDB table from the TABLE_NAME environment variable. We’ll define that variable later during deployment.
4.3. Implementation of Function 1
In our first function, we want to demonstrate how to get a payload (like from a PUT or POST request) from the 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();
}
As discussed before, we’ll configure the API later to use Lambda proxy integration. We expect the API Gateway to pass the complete request to the Lambda function in the InputStream parameter.
All we have to do is to pick the relevant attributes from the contained JSON structure.
- Fetching the body object from our input stream and creating a Person object from that
- Storing that Person object in a DynamoDB table
- Building a JSON object, which can hold several attributes, like a body for the response, custom headers, as well as an HTTP status code
One point worth mentioning here: API Gateway expects the body to be a String (for both request and response).
As we expect to get a String as body from the API Gateway, we cast the body to String and initialize our Person object:
Person person = new Person((String) event.get("body"));
API Gateway also expects the response body to be a String:
responseJson.put("body", responseBody.toString());
This topic is not mentioned explicitly in the official documentation. However, if we have a close look, we can see that the body attribute is a String in both snippets for the API Gateway simple proxy for lambda input format and for the API gateway simple proxy for lambda output format.
The advantage should be clear: even if JSON is the format between API Gateway and the Lambda function, the actual body can contain plain text, JSON, XML, or whatever. It is then the responsibility of the Lambda function to handle the format correctly.
We’ll see how the request and response body look later when we test our functions in the AWS Console.
The same also applies to the following two functions.
4.4. Implementation of Function 2
In a second step, we want to demonstrate how to use a path parameter or a query string parameter for retrieving a Person item from the database using its ID:
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();
}
Again, three steps are relevant:
- We check whether a pathParameters or an queryStringParameters array with an id attribute are present.
- If true, we use the belonging value to request a Person item with that ID from the database.
- We add a JSON representation of the received item to the response.
The official documentation provides a more detailed explanation of input format and output format for Proxy Integration.
4.5. Building Code
Again, we can simply build our code using Maven:
mvn clean package shade:shade
The JAR file will be created under the target folder.
4.6. Creating the DynamoDB Table
We can create the table as explained in AWS Lambda Using DynamoDB With Java.
Let’s choose Person as table name, id as primary key name, and Number as type of the primary key.
4.7. Deploying Code via AWS Console
After building our code and creating the table, we can now create the functions and upload the code.
This can be done by repeating steps 1-5 from the AWS Lambda with Java article, one time for each of our two methods.
Let’s use the following function names:
- StorePersonFunction for the handleRequest method (function 1)
- GetPersonByHTTPParamFunction for the handleGetByParam method (function 2)
We also have to define an environment variable TABLE_NAME with value “Person”.
4.8. Testing the Functions
Before continuing with the actual API Gateway part, we can run a quick test in the AWS Console, just to check that our Lambda functions are running correctly and can handle the Proxy Integration format.
Testing a Lambda function from the AWS Console works as described in AWS Lambda with Java article.
However, when we create a test event, we have to consider the special Proxy Integration format, which our functions are expecting. We can either use the API Gateway AWS Proxy template and customize that for our needs, or we can copy and paste the following events:
For the StorePersonFunction, we should use this:
{
"body": "{\"id\": 1, \"name\": \"John Doe\"}"
}
As discussed before, the body must have the type String, even if containing a JSON structure. The reason is that the API Gateway will send its requests in the same format.
The following response should be returned:
{
"headers": {
"x-custom-header": "my custom header value"
},
"body": "{\"message\":\"New item created\"}",
"statusCode": 200
}
Here, we can see that the body of our response is a String, although it contains a JSON structure.
5. Creating and Testing the API
After we created and deployed the Lambda functions in the previous section, we can now create the actual API using the AWS Console.
Let’s look at the basic workflow:
- Create an API in our AWS account.
- Add a resource to the resources hierarchy of the API.
- Create one or more methods for the resource.
- Set up the integration between a method and the belonging Lambda function.
We’ll repeat steps 2-4 for each of our two functions in the following sections.
5.1. Creating the API
For creating the API, we’ll have to:
- Sign in to the API Gateway console at https://console.aws.amazon.com/apigateway
- Click on “Get Started” and then select “New API”
- Type in the name of our API (TestAPI) and acknowledge by clicking on “Create API”
Having created the API, we can now create the API structure and link it to our Lambda functions.
5.2. API Structure for Function 1
The following steps are necessary for our StorePersonFunction:
- Choose the parent resource item under the “Resources” tree and then select “Create Resource” from the “Actions” drop-down menu. Then, we have to do the following in the “New Child Resource” pane:
- Type “Persons” as a name in the “Resource Name” input text field
- Leave the default value in the “Resource Path” input text field
- Choose “Create Resource”
- Choose the resource just created, choose “Create Method” from the “Actions” drop-down menu, and carry out the following steps:
- Choose PUT from the HTTP method drop-down list and then choose the check mark icon to save the choice
- Leave “Lambda Function” as integration type, and select the “Use Lambda Proxy integration” option
- Choose the region from “Lambda Region”, where we deployed our Lambda functions before
- Type StorePersonFunction in “Lambda Function”
- Choose “Save” and acknowledge with “OK” when prompted with “Add Permission to Lambda Function”
5.3. API Structure for Function 2 – Path Parameters
The steps for our retrieving path parameters are similar:
- Choose the /persons resource item under the “Resources” tree and then select “Create Resource” from the “Actions” drop-down menu. Then, we have to do the following in the “New Child Resource” pane:
- Type Person as a name in the “Resource Name” input text field
- Change the “Resource Path” input text field to {id}
- Choose “Create Resource”
- Choose the resource just created, select “Create Method” from the “Actions” drop-down menu, and carry out the following steps:
- Choose GET from the HTTP method drop-down list and then choose the check mark icon to save the choice
- Leave “Lambda Function” as integration type, and select the “Use Lambda Proxy integration” option
- Choose the region from “Lambda Region”, where we deployed our Lambda functions before
- Type GetPersonByHTTPParamFunction in “Lambda Function”
- Choose “Save” and acknowledge with “OK” when prompted with “Add Permission to Lambda Function”
5.4. API Structure for Function 2 – Query String Parameters
The steps for receiving query string parameters are a bit different, as we don’t have to create a resource, but instead have to create a query parameter for the id parameter:
- Choose the /persons resource item under the “Resources” tree, select “Create Method” from the “Actions” drop-down menu, and carry out the following steps:
- Choose GET from the HTTP method drop-down list and then select the check mark icon to save the choice
- Leave “Lambda Function” as integration type, and select the “Use Lambda Proxy integration” option
- Choose the region from “Lambda Region”, where we deployed our Lambda functions before
- Type GetPersonByHTTPParamFunction in “Lambda Function”.
- Choose “Save” and acknowledge with “OK” when prompted with “Add Permission to Lambda Function”
- Choose “Method Request” on the right and carry out the following steps:
- Expand the URL Query String Parameters list
- Click on “Add Query String”
- Type id in the name field, and choose the check mark icon to save
- Select the “Required” checkbox
- Click on the pen symbol next to “Request validator” on the top of the panel, select “Validate query string parameters and headers”, and choose the check mark icon
5.5. Testing the API
Our API is now ready, but it’s not public yet. Before we publish it, we want to run a quick test from the Console first.
For that, we can select the respective method to be tested in the “Resources” tree and click on the “Test” button. On the following screen, we can type in our input, as we would send it with a client via HTTP.
For StorePersonFunction, we have to type the following structure into the “Request Body” field:
{
"id": 2,
"name": "Jane Doe"
}
For the GetPersonByHTTPParamFunction with path parameters, we have to type 2 as a value into the {id} field under “Path”.
For the GetPersonByHTTPParamFunction with query string parameters, we have to type id=2 as a value into the {persons} field under “Query Strings”.
5.6. Deploying the API
Up to now, our API wasn’t public and thereby was only available from the AWS Console.
As discussed before, when we deploy an API, we have to associate it with a stage, which is like a snapshot in time of the API. If we redeploy an API, we can either update an existing stage or create a new one
Let’s see how the URL scheme for our API will look:
https://{restapi-id}.execute-api.{region}.amazonaws.com/{stageName}
The following steps are required for deployment:
- Choose the particular API in the “APIs” navigation pane
- Choose “Actions” in the “Resources” navigation pane and select “Deploy API” from the “Actions” drop-down menu
- Choose “[New Stage]” from the “Deployment stage” drop-down, type test in Stage name, and optionally provide a description of the stage and deployment
- Trigger the deployment by choosing “Deploy”
After the last step, the console will provide the root URL of the API, for example, https://0skaqfgdw4.execute-api.eu-central-1.amazonaws.com/test
5.7. Invoking the Endpoint
As the API is public now, we can call it using any HTTP client we want.
With cURL, the calls would look like as follows.
StorePersonFunction:
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 for path parameters:
curl -X GET 'https://0skaqfgdw4.execute-api.eu-central-1.amazonaws.com/test/persons/3' \
-H 'content-type: application/json'
GetPersonByHTTPParamFunction for query string parameters:
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 Proxy Integration 集成 Lambda 函數。
最後,我們看到了如何創建、部署和測試 API。
一個 並將其與所謂的 stage 相關聯。一個階段就像 API 的時間快照。如果重新部署 API,我們可以更新現有階段或創建一個新階段。通過這樣,可以在同一時間對 API 的不同版本是可能的,例如 dev 階段、test 階段,甚至多個生產版本,如 v1、v2 等。
Lambda Proxy integration 是 Lambda 函數和 API Gateway 之間集成的簡易配置。
API Gateway 將整個請求作為輸入發送到後端 Lambda 函數。響應方面,API Gateway 將 Lambda 函數的輸出轉換為前端 HTTP 響應。
3. 依賴項
我們需要與在“使用Java的AWS Lambda與DynamoDB”文章中相同的依賴項。
除此之外,我們還需要JSON Simple庫:
<dependency>
<groupId>com.googlecode.json-simple</groupId>
<artifactId>json-simple</artifactId>
<version>1.1.1</version>
</dependency>
4. Developing and Deploying the Lambda Functions
In this section, we’ll develop and build our Lambda functions in Java, we’ll deploy it using AWS Console, and we’ll run a quick test.
As we want to demonstrate the basic capabilities of integrating API Gateway with Lambda, we’ll create two functions:
- Function 1: receives a payload from the API, using a PUT method
- Function 2: demonstrates how to use an HTTP path parameter or HTTP query parameter coming from the API
Implementation-wise, we’ll create one RequestHandler class, which has two methods— one for each function.
4.1. Model
Before we implement the actual request handler, let’s have a quick look at our data model:
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
}
Our model consists of one simple Person class, which has two properties. The only notable part is the Person(String) constructor, which accepts a JSON String.
4.2. Implementation of the RequestHandler Class
Just like in the AWS Lambda With Java article, we’ll create an implementation of the RequestStreamHandler interface:
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
}
}
As we can see, the RequestStreamHander interface defines only one method, handeRequest(). Anyhow, we can define further functions in the same class, as we’ve done here. Another option would be to create one implementation ofRequestStreamHander for each function.
In our specific case, we chose the former for simplicity. However, the choice must be made on a case-by-case basis, taking into consideration such factors as performance and code maintainability.
We also read the name of our DynamoDB table from the TABLE_NAME environment variable. We’ll define that variable later during deployment.
4.3. Implementation of Function 1
In our first function, we want to demonstrate how to get a payload (like from a PUT or POST request) from the 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();
}
As discussed before, we’ll configure the API later to use Lambda proxy integration. We expect the API Gateway to pass the complete request to the Lambda function in the InputStream parameter.
All we have to do is to pick the relevant attributes from the contained JSON structure.
- Fetching the body object from our input stream and creating a Person object from that
- Storing that Person object in a DynamoDB table
- Building a JSON object, which can hold several attributes, like a body for the response, custom headers, as well as an HTTP status code
One point worth mentioning here: API Gateway expects the body to be a String (for both request and response).
As we expect to get a String as body from the API Gateway, we cast the body to String and initialize our Person object:
Person person = new Person((String) event.get("body"));
API Gateway also expects the response body to be a String:
responseJson.put("body", responseBody.toString());
This topic is not mentioned explicitly in the official documentation. However, if we have a close look, we can see that the body attribute is a String in both snippets for the API Gateway simple proxy for lambda input format and for the API gateway simple proxy for lambda output format.
The advantage should be clear: even if JSON is the format between API Gateway and the Lambda function, the actual body can contain plain text, JSON, XML, or whatever. It is then the responsibility of the Lambda function to handle the format correctly.
We’ll see how the request and response body look later when we test our functions in the AWS Console.
The same also applies to the following two functions.
4.4. Implementation of Function 2
In a second step, we want to demonstrate how to use a path parameter or a query string parameter for retrieving a Person item from the database using its ID:
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();
}
Again, three steps are relevant:
- We check whether a pathParameters or an queryStringParameters array with an id attribute are present.
- If true, we use the belonging value to request a Person item with that ID from the database.
- We add a JSON representation of the received item to the response.
The official documentation provides a more detailed explanation of input format and output format for Proxy Integration.
4.5. Building Code
Again, we can simply build our code using Maven:
mvn clean package shade:shade
The JAR file will be created under the target folder.
4.6. Creating the DynamoDB Table
We can create the table as explained in AWS Lambda Using DynamoDB With Java.
Let’s choose Person as table name, id as primary key name, and Number as type of the primary key.
4.7. Deploying Code via AWS Console
After building our code and creating the table, we can now create the functions and upload the code.
This can be done by repeating steps 1-5 from the AWS Lambda with Java article, one time for each of our two methods.
Let’s use the following function names:
- StorePersonFunction for the handleRequest method (function 1)
- GetPersonByHTTPParamFunction for the handleGetByParam method (function 2)
We also have to define an environment variable TABLE_NAME with value “Person”.
4.8. Testing the Functions
Before continuing with the actual API Gateway part, we can run a quick test in the AWS Console, just to check that our Lambda functions are running correctly and can handle the Proxy Integration format.
Testing a Lambda function from the AWS Console works as described in AWS Lambda with Java article.
However, when we create a test event, we have to consider the special Proxy Integration format, which our functions are expecting. We can either use the API Gateway AWS Proxy template and customize that for our needs, or we can copy and paste the following events:
For the StorePersonFunction, we should use this:
{
"body": "{\"id\": 1, \"name\": \"John Doe\"}"
}
As discussed before, the body must have the type String, even if containing a JSON structure. The reason is that the API Gateway will send its requests in the same format.
The following response should be returned:
{
"headers": {
"x-custom-header": "my custom header value"
},
"body": "{\"message\":\"New item created\"}",
"statusCode": 200
}
Here, we can see that the body of our response is a String, although it contains a JSON structure.
5. Creating and Testing the API
After we created and deployed the Lambda functions in the previous section, we can now create the actual API using the AWS Console.
Let’s look at the basic workflow:
- Create an API in our AWS account.
- Add a resource to the resources hierarchy of the API.
- Create one or more methods for the resource.
- Set up the integration between a method and the belonging Lambda function.
We’ll repeat steps 2-4 for each of our two functions in the following sections.
5.1. Creating the API
For creating the API, we’ll have to:
- Sign in to the API Gateway console at https://console.aws.amazon.com/apigateway
- Click on “Get Started” and then select “New API”
- Type in the name of our API (TestAPI) and acknowledge by clicking on “Create API”
Having created the API, we can now create the API structure and link it to our Lambda functions.
5.2. API Structure for Function 1
The following steps are necessary for our StorePersonFunction:
- Choose the parent resource item under the “Resources” tree and then select “Create Resource” from the “Actions” drop-down menu. Then, we have to do the following in the “New Child Resource” pane:
- Type “Persons” as a name in the “Resource Name” input text field
- Leave the default value in the “Resource Path” input text field
- Choose “Create Resource”
- Choose the resource just created, choose “Create Method” from the “Actions” drop-down menu, and carry out the following steps:
- Choose PUT from the HTTP method drop-down list and then choose the check mark icon to save the choice
- Leave “Lambda Function” as integration type, and select the “Use Lambda Proxy integration” option
- Choose the region from “Lambda Region”, where we deployed our Lambda functions before
- Type StorePersonFunction in “Lambda Function”
- Choose “Save” and acknowledge with “OK” when prompted with “Add Permission to Lambda Function”
5.3. API Structure for Function 2 – Path Parameters
The steps for our retrieving path parameters are similar:
- Choose the /persons resource item under the “Resources” tree and then select “Create Resource” from the “Actions” drop-down menu. Then, we have to do the following in the “New Child Resource” pane:
- Type Person as a name in the “Resource Name” input text field
- Change the “Resource Path” input text field to {id}
- Choose “Create Resource”
- Choose the resource just created, select “Create Method” from the “Actions” drop-down menu, and carry out the following steps:
- Choose GET from the HTTP method drop-down list and then choose the check mark icon to save the choice
- Leave “Lambda Function” as integration type, and select the “Use Lambda Proxy integration” option
- Choose the region from “Lambda Region”, where we deployed our Lambda functions before
- Type GetPersonByHTTPParamFunction in “Lambda Function”
- Choose “Save” and acknowledge with “OK” when prompted with “Add Permission to Lambda Function”
5.4. API Structure for Function 2 – Query String Parameters
The steps for receiving query string parameters are a bit different, as we don’t have to create a resource, but instead have to create a query parameter for the id parameter:
- Choose the /persons resource item under the “Resources” tree, select “Create Method” from the “Actions” drop-down menu, and carry out the following steps:
- Choose GET from the HTTP method drop-down list and then select the check mark icon to save the choice
- Leave “Lambda Function” as integration type, and select the “Use Lambda Proxy integration” option
- Choose the region from “Lambda Region”, where we deployed our Lambda functions before
- Type GetPersonByHTTPParamFunction in “Lambda Function”.
- Choose “Save” and acknowledge with “OK” when prompted with “Add Permission to Lambda Function”
- Choose “Method Request” on the right and carry out the following steps:
- Expand the URL Query String Parameters list
- Click on “Add Query String”
- Type id in the name field, and choose the check mark icon to save
- Select the “Required” checkbox
- Click on the pen symbol next to “Request validator” on the top of the panel, select “Validate query string parameters and headers”, and choose the check mark icon
5.5. Testing the API
Our API is now ready, but it’s not public yet. Before we publish it, we want to run a quick test from the Console first.
For that, we can select the respective method to be tested in the “Resources” tree and click on the “Test” button. On the following screen, we can type in our input, as we would send it with a client via HTTP.
For StorePersonFunction, we have to type the following structure into the “Request Body” field:
{
"id": 2,
"name": "Jane Doe"
}
For the GetPersonByHTTPParamFunction with path parameters, we have to type 2 as a value into the {id} field under “Path”.
For the GetPersonByHTTPParamFunction with query string parameters, we have to type id=2 as a value into the {persons} field under “Query Strings”.
5.6. Deploying the API
Up to now, our API wasn’t public and thereby was only available from the AWS Console.
As discussed before, when we deploy an API, we have to associate it with a stage, which is like a snapshot in time of the API. If we redeploy an API, we can either update an existing stage or create a new one
Let’s see how the URL scheme for our API will look:
https://{restapi-id}.execute-api.{region}.amazonaws.com/{stageName}
The following steps are required for deployment:
- Choose the particular API in the “APIs” navigation pane
- Choose “Actions” in the “Resources” navigation pane and select “Deploy API” from the “Actions” drop-down menu
- Choose “[New Stage]” from the “Deployment stage” drop-down, type test in Stage name, and optionally provide a description of the stage and deployment
- Trigger the deployment by choosing “Deploy”
After the last step, the console will provide the root URL of the API, for example, https://0skaqfgdw4.execute-api.eu-central-1.amazonaws.com/test
5.7. Invoking the Endpoint
As the API is public now, we can call it using any HTTP client we want.
With cURL, the calls would look like as follows.
StorePersonFunction:
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 for path parameters:
curl -X GET 'https://0skaqfgdw4.execute-api.eu-central-1.amazonaws.com/test/persons/3' \
-H 'content-type: application/json'
GetPersonByHTTPParamFunction for query string parameters:
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 Proxy Integration 集成 Lambda 函數。
最後,我們看到了如何創建、部署和測試 API。