AWS API Gateway 集成使得使用符合 OpenAPI 标准的 Lambda Function 轻松实现 REST API。
关于开放API
它是一个允许以标准方式描述 REST API 的规范。OpenAPI规范 (OAS) 为 REST API 定义了与编程语言无关的标准接口描述。这使得人类和计算机都可以发现和理解服务的功能,而无需访问源代码、附加文档或检查网络流量。
关于Swagger
Swagger是一组围绕 OpenAPI 规范构建的开源工具,可以帮助软件专业人员设计、构建、记录和使用 REST API。OpenAPI 处理规范,Swagger 实现规范来描述 REST API 。
OpenAPI规范简介
目的
- API是使用OpenAPI标准定义的,可以被人类和计算机发现,无需访问源代码、文档或通过网络流量检查即可了解服务的功能。
- 如果定义正确,消费者可以使用最少量的实现逻辑来理解远程服务并与之交互。
- 然后,文档生成工具可以使用 OpenAPI 定义来显示 API,代码生成工具可以使用各种编程语言、测试工具和许多其他用例来生成服务器和客户端。
开放API的 主要 概念
OpenAPI文档
定义/描述 API 的文档(或一组文档)。OpenAPI 定义使用并符合 OpenAPI 规范。
路径模板
路径模板是指使用由大括号 ({}) 分隔的模板表达式,将 URL 路径的一部分标记为可使用路径参数进行替换。
路径中的每个模板表达式必须对应于路径项本身和/或路径项的每个操作中包含的路径参数。
媒体类型
媒体类型定义分布在多个资源中。媒体类型定义应符合RFC6838。
可能的媒体类型定义的一些示例:
- 应用程序/json
- 测试/普通;chartset-uff-8
HTTP 状态代码
HTTP 状态代码用于指示已执行操作的状态。可用的状态代码由RFC7231定义,注册的状态代码列在IANA 状态代码注册表中。
AWS Lambda 函数的开放 API 参考实现
参考架构
参考实现由以下组件组成:
- AWS Lambda函数实现业务需求/逻辑
- API 网关将功能公开为 API
- Amazon API 网关集成器,用于集成开放 API 规范并将 Lambda 函数操作公开为 API
有关每个架构组件的更多详细信息,请参阅后续部分。
关于 AWS Lambda 函数
Lambda 是一种计算服务,允许在不配置或管理服务器的情况下运行代码。Lambda 在高可用性计算基础设施上运行代码,并执行计算资源的所有管理,包括服务器和操作系统维护、容量配置和自动扩展以及日志记录。使用 Lambda,几乎可以为任何类型的应用程序或后端服务运行代码。它所需要的只是以Lambda 支持的语言之一提供代码。
Lambda 仅在需要时运行函数并自动扩展,从每天几个请求到每秒数千个请求。它节省了计算成本,因为只需支付函数在运行时消耗的计算时间------代码不运行时不收费。可以使用 Lambda API 或来自其他 AWS 服务的事件调用 lambda 函数,例如在 S3 存储桶中创建对象时。
关于 AWS API 网关
Amazon API Gateway 是一项完全托管的服务,使开发人员可以轻松创建、发布、维护、监控和保护任何规模的 API。API 充当应用程序从后端服务访问数据、业务逻辑或功能的"前门"。
关于AWS API集成功能
这种类型的集成允许 API 公开 AWS 服务操作。在AWS集成中,需要配置集成请求和集成响应,并设置从方法请求到集成请求、从集成响应到方法响应的必要数据映射。
它允许指定用于此方法的后端集成的详细信息。此扩展是OpenAPI 操作对象的扩展属性。结果是一个API 网关集成对象。
重要财产
与指定后端的集成类型。有效值为:
- http 或 http_proxy,用于与 HTTP 后端集成。
- aws_proxy,用于与 AWS Lambda 函数的代理集成
- aws,用于与 AWS Lambda 函数自定义集成,或与其他 AWS 服务集成,例如 Amazon DynamoDB、Amazon Simple notification Service 或 Amazon Simple Queue Service。
- mock,用于与 API Gateway 集成,无需调用任何后端。这种类型的集成允许 API Gateway 返回响应,而无需将请求进一步发送到后端。
Lambda 代理集成支持单个 Lambda 函数的简化集成设置。设置很简单,可以随后端一起发展,而无需拆除已经创建的设置。由于这些原因,强烈建议与 Lambda 函数集成。
相比之下,Lambda 自定义集成允许为具有类似输入和输出数据格式要求的各种集成端点重用已配置的映射模板。该设置涉及较多,推荐用于更高级的应用场景。
x-amazon-apigateway-integrations
例子
以下示例使用 OpenAPI 标准定义 HTTP API(一个 GET 和一个 POST),并为每个 API 使用一个 API 网关集成来与 Lambda Function 集成(通过引用函数的 ARN)。它使用 AWS Identity and Access Management (IAM) 角色作为集成凭证。以下格式采用 YAML。
openapi: 3.0.1
info:
description: "This is a definition of Proxy Pattern Service. The service has 2 APIs."
version: v1
title: "Proxy Pattern Service"
paths:
/v1/proxypattern/employee:
options:
summary: CORS support
description: |
Enable CORS by returning correct headers
consumes:
- application/json
produces:
- application/json
tags:
- CORS
security:
- NONE: []
x-amazon-apigateway-integration:
type: mock
requestTemplates:
application/json: |
{
"statusCode" : 200
}
responses:
"default":
statusCode: "200"
responseParameters:
method.response.header.Access-Control-Allow-Headers : "'Content-Type,X-Amz-Date,Authorization,X-Api-Key,x-apigw-api-id,X-Amz-Security-Token,Cache-Control'"
method.response.header.Access-Control-Allow-Methods : "'*'"
method.response.header.Access-Control-Allow-Origin : "'*'"
responseTemplates:
application/json: |
{}
responses:
200:
description: Default response for CORS method
headers:
Access-Control-Allow-Headers:
type: "string"
Access-Control-Allow-Methods:
type: "string"
Access-Control-Allow-Origin:
type: "string"
post:
summary: "Save Employee"
operationId: "saveemployees"
tags:
- saveemployees
requestBody:
required: true
content:
application/json:
schema:
$ref: '#/components/schemas/employee'
responses:
'200':
description: "Saved Employee Successfully"
headers:
Access-Control-Allow-Origin:
schema:
type: "string"
content:
application/json:
schema:
$ref: "#/components/schemas/employee"
'400':
description: "Application Errors"
headers:
Access-Control-Allow-Origin:
schema:
type: "string"
content:
application/json:
schema:
$ref: '#/components/schemas/ErrorResponse'
'500':
description: "Other unspecified Errors"
headers:
Access-Control-Allow-Origin:
schema:
type: "string"
content:
application/json:
schema:
$ref: '#/components/schemas/ErrorResponse'
x-amazon-apigateway-integration:
credentials:
Fn::Sub: arn:aws:iam::${AWS::AccountId}:role/delegate-admin-lambda-proxy-pattern-role
httpMethod: "POST"
uri:
Fn::Sub: arn:aws:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/${ProxyPatternService.Arn}:live/invocations
responses:
"default":
statusCode: "200"
responseParameters:
method.response.header.Access-Control-Allow-Origin : "'*'"
"BAD.*":
statusCode: "400"
responseParameters:
method.response.header.Access-Control-Allow-Origin : "'*'"
"INT.*":
statusCode: "500"
responseParameters:
method.response.header.Access-Control-Allow-Origin : "'*'"
type: "aws_proxy"
get:
summary: "Get All the Employees"
operationId: "getemployees"
tags:
- getemployees
responses:
'200':
description: "All The Employees retrieved successfully"
headers:
Access-Control-Allow-Origin:
schema:
type: "string"
content:
application/json:
schema:
type: "array"
items:
$ref: "#/components/schemas/employee"
'204':
description: "Employees not found"
headers:
Access-Control-Allow-Origin:
schema:
type: "string"
'500':
description: "Other unspecified Errors"
headers:
Access-Control-Allow-Origin:
schema:
type: "string"
content:
application/json:
schema:
$ref: '#/components/schemas/ErrorResponse'
x-amazon-apigateway-integration:
credentials:
Fn::Sub: arn:aws:iam::${AWS::AccountId}:role/delegate-admin-lambda-proxy-pattern-role
httpMethod: "POST"
uri:
Fn::Sub: arn:aws:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/${ProxyPatternService.Arn}:live/invocations
responses:
"default":
statusCode: "200"
responseParameters:
method.response.header.Access-Control-Allow-Origin : "'*'"
"BAD.*":
statusCode: "204"
responseParameters:
method.response.header.Access-Control-Allow-Origin : "'*'"
"INT.*":
statusCode: "500"
responseParameters:
method.response.header.Access-Control-Allow-Origin : "'*'"
type: "aws_proxy"
components:
schemas:
Employee:
type: object
description: EMPLOYEE
properties:
employeeId:
type: string
description: Id of the Employee
employeeName:
type: string
description: Name of the Employee
required:
- employeeId
ErrorResponse:
type: "object"
properties:
errorCode:
type: string
description: |
indicates an error in processing.
XXXX - Error in saving message
errorMessage:
type: string
description: "message description of error."
以下是 AWS Lambda 函数的云形成模板,该模板将通过 AWS API Gateway 公开。格式为 YAML。此处,已引用OpenAPI定义/openapi-apigateway-PxyPtrnSvc.yaml文件 ( ),以便API Gateway了解通过 OpenAPI 定义文件定义的集成点。
AWSTemplateFormatVersion: '2010-09-09'
Transform: AWS::Serverless-2016-10-31
Description: AWS Serverless Quarkus HTTP - Proxy Pattern Poc Service
Globals:
Api:
EndpointConfiguration:
Type: PRIVATE
Auth:
ResourcePolicy:
CustomStatements: {
Effect: 'Allow',
Action: 'execute-api:Invoke',
Resource: ['execute-api:/*/*/*'],
Principal: '*'
}
Resources:
ProxyPatternServiceAWSApiGateway:
Type: AWS::Serverless::Api
Properties:
TracingEnabled: true
Name: ProxyPatternServiceSvcApiGateway
StageName: dev
DefinitionBody:
'Fn::Transform':
Name: 'AWS::Include'
Parameters:
Location: './openapi-apigateway-PxyPtrnSvc.yaml'
ProxyPatternServiceLogGroup:
Type: AWS::Logs::LogGroup
Properties:
LogGroupName: '/aws/lambda/ProxyPatternService'
RetentionInDays: 30
ProxyPatternService:
Type: AWS::Serverless::Function
Properties:
Handler: not.used.in.provided.runtime
Runtime: provided
CodeUri: component1/function.zip
MemorySize: 512
Timeout: 900
FunctionName: ProxyPatternService
Environment:
Variables:
LOG_LEVEL: INFO
DISABLE_SIGNAL_HANDLERS: true
Role: !Sub "arn:aws:iam::${AWS::AccountId}:role/delegate-admin-lambda-proxy-pattern-role"
Tracing: Active
Outputs:
ProxyPatternServiceAWSApiGateway:
Description: 'API Gateway endpoint URL for dev stage for Proxy Pattern Service Template'
Value: !Sub 'https://${ProxyPatternServiceAWSApiGateway}.execute-api.${AWS::Region}.amazonaws.com/dev/'
ProxyPatternServiceAWSApiGatewayRestApiId:
Description: 'API Gateway ARN for Basic AWS API Gateway'
Value: !Ref ProxyPatternServiceAWSApiGateway
Export:
Name: ProxyPatternServiceAWSApiGateway-RestApiId
ProxyPatternServiceAWSApiGatewayRootResourceId:
Value: !GetAtt ProxyPatternServiceAWSApiGateway.RootResourceId
Export:
Name: ProxyPatternServiceAWSApiGateway-RootResourceId
示例 API 定义可以与以下使用 Java 的 Controller 接口相关,并且可以在 Java Quarkus 中实现:
package com.example.proxypattern.controller;
import com.example.proxypattern.exception.model.ErrorResponse;
import com.example.proxypattern.model.Employee;
import org.eclipse.microprofile.openapi.annotations.Operation;
import org.eclipse.microprofile.openapi.annotations.media.Content;
import org.eclipse.microprofile.openapi.annotations.media.Schema;
import org.eclipse.microprofile.openapi.annotations.responses.APIResponse;
import org.eclipse.microprofile.openapi.annotations.responses.APIResponses;
import javax.ws.rs.*;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
@Path("v1/proxypattern/")
public interface IProxyPatternController {
@GET
@Path("/employee")
@Produces(MediaType.APPLICATION_JSON)
@Operation(summary = "Get All the Employees", description = "getEmployees")
@APIResponses(value = {
@APIResponse(responseCode = "204", description = "Employees not found"),
@APIResponse(responseCode = "200", description = "All The Employees retrieved successfully", content = @Content(schema = @Schema(implementation = Employee.class))),
@APIResponse(responseCode = "500", description = "Other unspecified Errors", content = @Content(schema = @Schema(implementation = ErrorResponse.class))) })
public Response getAllEmployee();
@POST
@Path("/employee")
@Consumes(MediaType.APPLICATION_JSON)
@Produces(MediaType.APPLICATION_JSON)
@Operation(summary = "Save Employee", description = "saveEmployees")
@APIResponses(value = {
@APIResponse(responseCode = "400", description = "Application Errors", content = @Content(schema = @Schema(implementation = ErrorResponse.class))),
@APIResponse(responseCode = "200", description = "Saved Employee Successfully", content = @Content(schema = @Schema(implementation = Employee.class))),
@APIResponse(responseCode = "500", description = "Other unspecified Errors", content = @Content(schema = @Schema(implementation = ErrorResponse.class))) })
public Response saveEmployee(Employee employee);
}
实现语言可能有所不同。如果技术决策是使用Java作为编码语言,则可以选择Java Quarkus作为Lambda函数的实现语言。Lambda 支持自定义运行时,因此可以本地构建 Quarkus 代码来解决与 Lambda 冷启动相关的响应时间问题。
控制器实现代码如下所示。这里,业务逻辑已通过业务服务类进行抽象:
package com.example.proxypattern.controller.impl;
import com.example.proxypattern.controller.IProxyPatternController;
import com.example.proxypattern.model.Employee;
import com.example.proxypattern.service.impl.EmployeeServiceImpl;
import lombok.extern.slf4j.Slf4j;
import org.eclipse.microprofile.opentracing.Traced;
import javax.inject.Inject;
import javax.inject.Singleton;
import javax.ws.rs.core.Response;import java.util.List;
@Slf4j
@Traced
@Singleton
public class ProxyPatternControllerImpl implements IProxyPatternController {
@Inject
EmployeeServiceImpl employeeService;
@Override
public Response getAllEmployee() {
Response.Status status = Response.Status.OK;
//The business logic is abstracted here in Service code
List<Employee> listOfEmployee = employeeService.getAllEmployee();
if(listOfEmployee.isEmpty()){
status = Response.Status.NO_CONTENT;
}
log.info("Sending employee {}", listOfEmployee);
return Response.status(status).entity(listOfEmployee).build();
}
@Override
public Response saveEmployee(Employee employee) {
//The business logic is abstracted here in Service code
Employee employeeResponse = employeeService.saveEmployee(employee);
return Response.status(Response.Status.CREATED).entity(employeeResponse).build();
}
}
可以使用Maven来构建项目,也可以使用以下依赖来构建项目:
<dependencies>
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-resteasy</artifactId>
</dependency>
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-amazon-lambda-rest</artifactId>
</dependency>
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-smallrye-openapi</artifactId>
</dependency>
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-resteasy-jackson</artifactId>
</dependency>
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-logging-json</artifactId>
</dependency>
<dependency>
<groupId>org.jboss.slf4j</groupId>
<artifactId>slf4j-jboss-logmanager</artifactId>
</dependency>
<dependency>
<groupId>commons-logging</groupId>
<artifactId>commons-logging</artifactId>
<version>1.2</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-annotations</artifactId>
</dependency>
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-hibernate-validator</artifactId>
</dependency>
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-smallrye-opentracing</artifactId>
</dependency>
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-smallrye-health</artifactId>
</dependency>
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-junit5</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>io.rest-assured</groupId>
<artifactId>rest-assured</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-junit5-mockito</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.jacoco</groupId>
<artifactId>org.jacoco.agent</artifactId>
<classifier>runtime</classifier>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.20</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
</dependency>
</dependencies>
结论
AWS API Gateway 集成可以轻松使用 Lambda Function 实现 REST API,该函数符合 OpenAPI 标准,并且可以轻松与 AWS API Gateway 集成。