无服务器 – 带有 DynamoDB 的 REST API
无服务器 – 带有 DynamoDB 的 REST API
到目前为止,我们已经学习了几个与无服务器 lambda 部署相关的概念。现在是看一些例子的时候了。在本章中,我们将查看 Serverless 官方提供的示例之一。顾名思义,我们将创建一个 REST API。正如您所猜到的,我们所有的 lambda 函数都将由 API 网关触发。我们的 lambda 函数将与 dynamoDB 表进行交互,该表本质上是一个待办事项列表,用户将能够使用端点执行多项操作,例如创建新项目、获取现有项目、删除项目等。在部署后公开。如果您不熟悉 REST API,那么您可以在此处阅读有关它们的更多信息。
代码演练
代码可以在 GitHub 上找到 – https://github.com/serverless/examples/tree/master/aws-python-rest-api-with-dynamodb
我们将看一下项目结构,讨论一些我们目前还没有看到的新概念,然后执行 serverless.yml 文件的演练。所有函数处理程序的演练将是多余的。因此,我们将只介绍一个函数处理程序。您可以将理解其他功能作为练习。
项目结构
现在,如果您查看项目结构,则 lambda 函数处理程序都位于 todos 文件夹中的单独 .py 文件中。serverless.yml 文件在每个函数处理程序的路径中指定了 todos 文件夹。没有外部依赖项,因此没有 requirements.txt 文件。
新概念
现在,您可能第一次看到几个术语。让我们快速扫描这些 –
-
dynamoDB – 这是 AWS 提供的 NoSQL(不仅是 SQL)数据库。虽然不完全准确,但从广义上讲,NoSQL 之于 SQL,就像 Word 之于 Excel。您可以在此处阅读有关 NoSQL 的更多信息。NoSQL 数据库有 4 种类型 – 文档数据库、键值数据库、宽列存储和图形数据库。dynamoDB 是一个键值数据库,这意味着您可以不断地将键值对插入到数据库中。这类似于redis缓存。您可以通过引用其键来检索该值。
-
boto3 – 这是适用于 Python 的 AWS 开发工具包。如果您需要在 lambda 函数中配置、管理、调用或创建 AWS 的任何服务(EC2、dynamoDB、S3 等),则需要 boto3 SDK。您可以在此处阅读有关 boto3 的更多信息。
除此之外,我们在演练 serverless.yml 和处理程序函数的过程中还会遇到一些概念。我们将在那里讨论它们。
serverless.yml 演练
serverless.yml 文件以服务的定义开始。
service: serverless-rest-api-with-dynamodb
然后通过以下行声明框架版本范围 –
frameworkVersion: ">=1.1.0 <=2.1.1"
这就像支票一样。如果您的无服务器版本不在此范围内,则会引发错误。当您共享代码并希望使用此 serverless.yml 文件的每个人都使用相同的 serverless 版本范围以避免出现问题时,这会有所帮助。
接下来,在提供程序中,我们看到了两个迄今为止尚未遇到的额外字段 – environment和iamRoleStatements。
provider: name: aws runtime: python3.8 environment: DYNAMODB_TABLE: ${self:service}-${opt:stage, self:provider.stage} iamRoleStatements: - Effect: Allow Action: - dynamodb:Query - dynamodb:Scan - dynamodb:GetItem - dynamodb:PutItem - dynamodb:UpdateItem - dynamodb:DeleteItem Resource: "arn:aws:dynamodb:${opt:region, self:provider.region}: *:table/${self:provider.environment.DYNAMODB_TABLE}"
Environment,如您所料,用于定义环境变量。此 serverless.yml 文件中定义的所有函数都可以获取这些环境变量。我们将在下面的函数处理程序演练中看到一个示例。在这里,我们将 dynamoDB 表名称定义为环境变量。
该$符号表示的变量。该自我关键字指serverless.yml文件本身,而选择是指一个选项,我们可以在提供SLS部署。因此,表名将是服务名称,后跟一个连字符,然后是文件找到的第一个阶段参数:无服务器部署期间的选项可用,或提供者阶段,默认情况下为dev。因此,在这种情况下,如果您在无服务器部署期间不提供任何选项,则 dynamoDB 表名称将为 serverless-rest-api-with-dynamodb-dev。您可以在此处阅读有关无服务器变量的更多信息。
iamRoleStatements定义提供给函数的权限。在这种情况下,我们允许函数对 dynamoDB 表执行以下操作 – Query、Scan、GetItem、PutItem、UpdateItem和DeleteItem。资源名称指定允许这些操作的确切表。如果您输入了“*”代替资源名称,您将允许对所有表进行这些操作。但是,在这里,我们希望只允许对一个表进行这些操作,因此,使用标准 arn 格式在资源名称中提供了该表的 arn(Amazon 资源名称)。这里再次使用选项区域(在无服务器部署期间指定)或提供程序中提到的区域(默认为 us-east-1)中的第一个区域。
在函数部分,函数是按照标准格式定义的。请注意,get、update、delete 都具有相同的路径,其中 id 作为路径参数。但是,每个方法都不同。
functions: create: handler: todos/create.create events: - http: path: todos method: post cors: true list: handler: todos/list.list events: - http: path: todos method: get cors: true get: handler: todos/get.get events: - http: path: todos/{id} method: get cors: true update: handler: todos/update.update events: - http: path: todos/{id} method: put cors: true delete: handler: todos/delete.delete events: - http: path: todos/{id} method: delete cors: true
后来,我们遇到了另一个我们以前没有见过的块,资源块。此块基本上可以帮助您在 CloudFormation 模板中指定需要创建的资源,以使函数正常工作。在这种情况下,我们需要为函数创建一个 dynamoDB 表。到目前为止,我们已经指定了表的名称,甚至引用了它的 ARN。但是我们还没有创建表。在资源块中指定表的特征将为我们创建该表。
resources: Resources: TodosDynamoDbTable: Type: 'AWS::DynamoDB::Table' DeletionPolicy: Retain Properties: AttributeDefinitions: - AttributeName: id AttributeType: S KeySchema: - AttributeName: id KeyType: HASH ProvisionedThroughput: ReadCapacityUnits: 1 WriteCapacityUnits: 1 TableName: ${self:provider.environment.DYNAMODB_TABLE}
这里定义了很多配置,其中大部分特定于 dynamoDB。简而言之,我们要求无服务器创建一个“TodosDynamoDbTable”,或输入“DynamoDB Table”,其中 TableName(在底部提到)等于在 provider 的环境变量中定义的那个。我们将其删除策略设置为“保留”,这意味着如果删除堆栈,则保留资源。见这里。我们说该表将有一个名为id的属性,其类型将为 String。我们还指定 id 属性将是 HASH 键或分区键。您可以在此处阅读有关 dynamoDB 表中 KeySchemas 的更多信息。最后,我们指定表的读取容量和写入容量。
就是这样!我们的 serverless.yml 文件现已准备就绪。现在,由于所有函数处理程序或多或少相似,我们将只介绍一个处理程序,即 create 函数的处理程序。
创建函数处理程序的演练
我们有几个导入语句
import json import logging import os import time import uuid
接下来,我们导入 boto3,如上所述,它是适用于 Python 的 AWS 开发工具包。我们需要 boto3 从 lambda 函数内与 dynamoDB 接口。
import boto3 dynamodb = boto3.resource('dynamodb')
接下来,在实际的函数处理程序中,我们首先检查“事件”有效负载的内容(create API 使用 post 方法)。如果它的正文不包含“文本”键,我们还没有收到要添加到待办事项列表的有效项目。因此,我们提出了一个例外。
def create(event, context): data = json.loads(event['body']) if 'text' not in data: logging.error("Validation Failed") raise Exception("Couldn't create the todo item.")
考虑到我们按预期获得了 ‘text’ 键,我们准备将其添加到 dynamoDB 表中。我们获取当前时间戳,并连接到 dynamoDB 表。注意 serverless.yml 中定义的环境变量是如何获取的(使用 os.environ)
timestamp = str(time.time()) table = dynamodb.Table(os.environ['DYNAMODB_TABLE'])
接下来,我们通过使用 uuid 包生成随机 uuid,使用接收到的数据作为文本,将 createdAt 和 updatedAt 设置为当前时间戳,并将字段 ‘checked’ 设置为 False 来创建要添加到表中的项目。‘checked’ 是另一个可以使用更新操作更新的字段,除了文本之外。
item = { 'id': str(uuid.uuid1()), 'text': data['text'], 'checked': False, 'createdAt': timestamp, 'updatedAt': timestamp, }
最后,我们将项目添加到 dynamoDB 表并将创建的项目返回给用户。
# write the todo to the database table.put_item(Item=item) # create a response response = { "statusCode": 200, "body": json.dumps(item) } return response
通过本演练,我认为其他函数处理程序将不言自明。在某些函数中,您可能会看到此语句 – “body” – json.dumps(result[‘Item’], cls=decimalencoder.DecimalEncoder)。这是用于json.dumps 中的错误的解决方法。json.dumps 默认不能处理十进制数,因此,decimalencoder.py文件已经被创建来包含处理这个的 DecimalEncoder 类。
恭喜您了解您使用无服务器创建的第一个综合项目。该项目的创建者还在README文件中分享了他的部署端点以及测试这些功能的方法。看一看。前往下一章查看另一个示例。