写点什么

借助 Serverless 框架构建 RESTful API

  • 2019-07-03
  • 本文字数:5865 字

    阅读完需:约 19 分钟

借助Serverless框架构建RESTful API

Serverless 应用程序已经存在了许多年,但是在过去的两年里,它的受欢迎程度直线上升。在本文中,你将了解如何从头构建 RESTful API 并将其部署到 AWS(Amazon Web Services)上。

什么是 Serverless 应用程序?

尽管名为“Serverless”,但它确实需要服务器来运行代码。关键的区别在于,你不需要管理运行代码的服务器,这消除了管理服务器、负载平衡器、应用补丁和扩展服务器的负担。


Serverless 应用程序可以在大多数云(AWS、Azure、GCP 和 IBM Cloud)上运行,但在本文中,我们将重点讨论 AWS,因为它是目前应用最广泛的云计算平台,尽管你学到的知识可以迁移到其他提供商。


Serverless 应用程序主要有四个部分组成:


  • 零管理

  • 自动扩展

  • 按使用付费

  • 提升速度

Serverless 框架

人们经常犯的一个错误是混淆了 Serverless 架构和框架的概念。Serverless 框架是一个开源 CLI 工具,它使代码部署变得更加容易且更可维护。它允许你将基础设施定义为代码(数据库、队列、文件存储、API 等),而不是手动登录并通过 Web 接口创建它们。


框架与云无关,被广泛采用,有良好的学习文档,并有一个大型的社区来支持它。

Serverless 框架的核心概念

使用 Serverless 框架开发 Serverless 应用程序有四个关键组件。


函数


函数是 AWS Lambda 函数,它是你编写业务逻辑的地方,它由事件调用。


常见函数举例:


  • 将数据保存到数据库中

  • 发送电子邮件

  • 处理文件


事件


任何触发函数运行的操作都被认为是一个事件。


常见事件举例:


  • AWS API 网关 HTTP 端点请求

  • AWS S3 桶上传

  • AWS SQS(简单队列服务)操作


资源


资源是你的函数所依赖的 AWS 基础设施。


常见的资源:


  • S3(处理文件)

  • 数据库(为了存储我们的数据,AWS 支持各种数据库技术)

  • SQS(队列)


服务


服务是框架的组织单元。你可以将它看作一个项目文件,尽管你可以为一个应用程序提供多个服务。它是定义函数、触发函数的事件和函数使用资源的地方,所有这些都在一个名为 serverless.yml 的文件中。

构建 API

在本教程中,你将构建一个图书 API,该 API 将图书保存到一个 NoSQL 数据存储(DynamoDB)中,并将用于管理图书的 CRUD(创建、读取、更新和删除)。


点击这里查看整个项目。

前提

  • 你的机器上已经安装了 Node.js

  • AWS 账号

项目设置

1)首先,你需要安装全局 Serverless 框架。


npm install -g serverless
复制代码


2)创建一个新目录“book-api”,并用你最喜欢的代码编辑器打开。


3)在项目根目录下运行如下命令生成新项目的框架。


serverless create --template aws-nodejs
复制代码


4)在项目根目录下新建一个文件“package.json”,并将下面的内容粘贴到这个文件中。


{  "name": "book-app",  "version": "1.0.0",  "description": "Serverless book management API",  "dependencies": {    "@hapi/joi": "^15.0.3",    "aws-sdk": "^2.466.0",    "uuid": "^3.3.2"  }}
复制代码


5)在项目的根目录下运行如下命令安装项目依赖。


npm install
复制代码


你的项目现在应该是下面这个样子:


book-api- node_modules- serverles.yml- handler.js- .gitignore- .package.json
复制代码

基础设施设置

Serverless 框架简化了在代码中定义基础设施的过程,你可以在“serverless.yml”中配置应用程序基础设施。当你部署代码时,配置将转换为 AWS 提供的 CloudFormation 模板,它允许你在代码中创建和管理基础设施。


要构建 API,你需要以下基础设施:


  • 数据库(在本指南中,你将使用由 AWS 开发的 NoSQL 数据库 DynamoDB)。

  • 安全策略(身份和访问管理是 AWS 提供的服务,允许你创建安全策略并将其分配给服务。你需要创建一个允许函数访问数据库的策略,因为默认情况下,服务是沙箱化的,这有助于减少漏洞和防止错误,比如删除生产数据库)。

  • 函数(处理 HTTP 请求并执行操作,如将数据项插入数据库并返回适当的 HTTP 响应)。

  • 事件(当接收到 HTTP 请求时调用函数)。


打开项目根目录下的文件“serverless.yml”,并用下面的内容替换。


service: book-api
provider: name: aws runtime: nodejs10.x stage: development region: eu-west-1 environment: BOOKS_TABLE: "books" iamRoleStatements: - Effect: Allow Action: - dynamodb:DescribeTable - dynamodb:Query - dynamodb:Scan - dynamodb:GetItem - dynamodb:PutItem - dynamodb:UpdateItem - dynamodb:DeleteItem Resource: Fn::Join: - "" - - "arn:aws:dynamodb:*:*:table/" - Ref: BooksTable
functions: create: handler: books/create.handler events: - http: path: books method: post cors: true
update: handler: books/update.handler events: - http: path: books/{id} method: put cors: true
list: handler: books/list.handler events: - http: path: books method: get cors: true
get: handler: books/get.handler events: - http: path: books/{id} method: get cors: true
delete: handler: books/delete.handler events: - http: path: books/{id} method: delete cors: true
resources: Resources: BooksTable: Type: AWS::DynamoDB::Table Properties: TableName: ${self:provider.environment.BOOKS_TABLE} AttributeDefinitions: - AttributeName: id AttributeType: S KeySchema: - AttributeName: id KeyType: HASH ProvisionedThroughput: ReadCapacityUnits: 1 WriteCapacityUnits: 1
复制代码


这个文件乍一看可能有点令人生畏,但让我们花点时间来消化代码,进一步了解每个部分在做什么。


Service


你的服务的名称,最好将其命名为描述性的名称,因为它在 AWS 的日志和各种其他位置中使用。


Provider


Provider 块是指定希望部署到的云平台和特定于给定云提供者的配置的地方。


  • name ——你希望的部署 API 的云提供商(AWS、Azure 等)。

  • runtime——运行时和版本(Node、GO、Python、.NET Core 等)。

  • stage ——部署阶段(开发、过渡、生产等)。

  • region——你希望的应用程序托管地区。

  • environment——全局环境变量,可以从函数里访问或者在配置文件中自引用。

  • iamRoleStatements——为 Lambda 函数指定安全策略,授予访问其他服务的权限。


Functions


这是指定函数和调用函数的事件的地方。正如你在上面的配置中所看到的,它指定了五个供各种请求类型的特定端点的 HTTP 事件调用的函数。


让我们看看其中一个函数,并试着理解它是如何工作的。


functions:  create:    handler: books/create.handler    events:      - http:          path: books          method: post          cors: true
复制代码


我们可以设想一下,我们的代码将被做处理如下:


  • 创建一个新的 AWS Lambda 函数,其标识符为“create”。

  • Lambda 函数的代码的位置为“books-api/books/create”。当事件触发时要调用的函数称为 handler。

  • 创建一个新事件,当你接收到路径为“/books”的 HTTP POST 请求时,该事件将运行 handler (AWS 使用 API 网关处理 HTTP 事件)。


Resources


这是指定应用程序所依赖的 AWS 基础设施的地方。正如你在配置中看到的,它告诉 AWS 新建一个名为“books”的 DynamoDB 表(通过自引用环境变量)。

创建图书模式

在将数据插入数据库之前验证数据始终是一种很好的实践,为了处理这个问题,你将使用一个名为“Joi”的开源模式验证器。


1)在项目根目录下创建一个新目录“books”。


2)在 books 目录下创建一个文件“schema.js”,并将如下内容粘贴到这个文件中。


const Joi = require("@hapi/joi");
const bookSchema = Joi.object().keys({ title: Joi.string() .min(1) .required(), author: Joi.string() .min(1) .required(), pages: Joi.number().required()});
function validateModel(model) { return Joi.validate(model, bookSchema, { abortEarly: false });};
module.exports = { validateModel};
复制代码


如你所见,我们定义了图书模式及其属性,并输出了一个函数“validateModel”,你将使用它来验证 handler 函数中的请求。

创建 handler 函数

现在是绑定 handler 函数的时候了,这些函数是在“serverless.yml”文件中指定的。你可能已经注意到,当你搭建项目时,它创建了一个名为“handler.js”的文件。我们不会使用这个,因为把所有的代码放在一个文件中是不好的做法,因为它变得非常复杂,打破了单一职责原则,你可以删除这个文件。

Create

在 books 目录下新建一个文件“create.js”,并将如下内容粘贴到这个文件中。


"use strict";const AWS = require("aws-sdk");const client = new AWS.DynamoDB.DocumentClient();const uuid = require("uuid");const { validateModel } = require("./schema");
module.exports.handler = async function createBook(event, context, callback) { const timestamp = new Date().getTime(); const data = JSON.parse(event.body);
const validation = validateModel(data);
if (validation.error) { const response = { statusCode: 400, body: JSON.stringify(validation.error.details) };
return callback(null, response); }
const params = { TableName: process.env.BOOKS_TABLE, Item: { id: uuid.v1(), created_at: timestamp, updated_at: timestamp, title: data.title, author: data.author, pages: data.pages } };
await client.put(params).promise();
const response = { statusCode: 201, body: JSON.stringify(params.Item) };
return callback(null, response);};
复制代码


上面的函数负责将图书保存到数据库中并以新创建的图书作为响应。


它可以分为以下几个步骤:


  1. 输出你在“serverless.yml”文件中 functions 块里引用的函数“handler”。

  2. 当接收到对“/books”的 HTTP Post 请求(到 API 网关)时,它将触发一个事件来运行 Lambda 函数并传递请求对象(事件参数的一部分)。

  3. 反序列化请求体并将其保存在“data”变量声明中。

  4. 验证模式,如果它无效,返回一个带有验证错误的错误请求。

  5. 创建一个参数对象,表名来自“serverless.yml”文件中声明的环境变量,Item 即数据库中的数据存储。

  6. 使用 AWS SDK 利用 params 对象将数据项“put”到 DynamoDB。

  7. 返回 201 HTTP 状态码(已创建),并将新创建的图书作为响应体发送。

Update

在 books 目录下新建一个文件“update.js”,并将如下内容粘贴到这个文件中。


"use strict";const AWS = require("aws-sdk");const client = new AWS.DynamoDB.DocumentClient();const { validateModel } = require("./schema");
module.exports.handler = async function updateBook(event, context, callback) { const timestamp = new Date().getTime(); const data = JSON.parse(event.body);
const validation = validateModel(data);
if (validation.error) { const response = { statusCode: 400, body: JSON.stringify(validation.error.details) };
return callback(null, response); }
const params = { TableName: process.env.BOOKS_TABLE, Key: { id: event.pathParameters.id }, ExpressionAttributeValues: { ":updated_at": timestamp, ":title": data.title, ":author": data.author, ":pages": data.pages }, UpdateExpression: "SET updated_at = :updated_at, title = :title, author = :author, pages = :pages", ReturnValues: "ALL_NEW" };
const result = await client.update(params).promise();
const response = { statusCode: 200, body: JSON.stringify(result.Attributes) };
return callback(null, response);};
复制代码

List

在 books 目录下新建一个文件“list.js”,并将如下内容粘贴到这个文件中。


"use strict";const AWS = require("aws-sdk");const client = new AWS.DynamoDB.DocumentClient();
module.exports.handler = async function listBooks(event, context, callback) { const params = { TableName: process.env.BOOKS_TABLE };
const { Items = [] } = await client.scan(params).promise();
callback(null, { statusCode: 200, body: JSON.stringify(Items) });};
复制代码

Get

在 books 目录下新建一个文件“get.js”,并将如下内容粘贴到这个文件中。


"use strict";const AWS = require("aws-sdk");const client = new AWS.DynamoDB.DocumentClient();
module.exports.handler = async function getBook(event, context, callback) { const params = { TableName: process.env.BOOKS_TABLE, Key: { id: event.pathParameters.id } };
const { Item } = await client.get(params).promise();
const response = { statusCode: Item ? 200 : 404, body: JSON.stringify(Item ? Item : { message: "Book not found!" }) };
callback(null, response);};
复制代码

Delete

在 books 目录下新建一个文件“delete.js”,并将如下内容粘贴到这个文件中。


"use strict";const AWS = require("aws-sdk");const client = new AWS.DynamoDB.DocumentClient();
module.exports.handler = async function deleteBook(event, context, callback) { const params = { TableName: process.env.BOOKS_TABLE, Key: { id: event.pathParameters.id } };
await client.delete(params).promise();
const response = { statusCode: 200 };
return callback(null, response);};
复制代码

部署

使用 Serverless 框架部署应用程序非常简单!这就是将基础设施作为代码的好处所在。


  1. 你需要将你的 AWS 帐户连接到你机器上的 Serverless 框架 CLI(这是一个一次性的过程)。

  2. 从应用程序的根目录运行以下命令:


serverless deplo
复制代码


3.你现在应该看到类似下面的屏幕截图:



4.现在,你可以将 HTTP 请求发送到终端中显示的端点(还可以从 AWS 控制台的“API 网关”选项卡下获取 URL)。


恭喜!你已经完成 Serverless 应用程序的部署!


英文原文:https://jamielivingstone.dev/build-a-rest-api-with-the-serverless-framework-and-deploy-to-aws


2019-07-03 19:336511
用户头像

发布了 742 篇内容, 共 481.2 次阅读, 收获喜欢 1549 次。

关注

评论

发布
暂无评论
发现更多内容

Java 创建线程有哪些方式

武哥聊编程

Java 多线程 28天写作

Serverless Kubernetes:理想,现实与未来

阿里巴巴云原生

Serverless 容器 运维 云原生 k8s

如何查看github评价

Albert

GitHub

【JS】预编译

德育处主任

JavaScript 大前端 js 28天写作

“大禹针”在北江大堤上线,浪潮助力广东水利新基建落地

新基建

云算力挖矿系统开发app,矿机租赁交易平台搭建

v16629866266

机器学习·笔记之:Cost Function - Intuition II

Nydia

LiteOS调测利器:backtrace函数原理知多少

华为云开发者联盟

架构 内存 函数 LiteOS backtrace

冲突域和广播域区别,集线器、交换机和路由器对比

创业失败启示录|老老王上的人生第一课

阿萌

28天写作 创业失败启示录 青城

阿里巴巴管理三板斧

Ian哥

28天写作

碎碎念之「程序员的时间都花在了哪?」

Justin

设计原则 代码规范 28天写作 技术债

微服务转型系列1:农商行数字化转型的烦恼

BoCloud博云

微服务 银行数字化转型 API 服务治理

28天瞎写的第二百三十一天:一次被骗的故事

树上

28天写作

谁能拯救终将凋落的头发「幻想短篇 21/28」

道伟

28天写作

大数据知识专栏 - MapReduce 的自定义分组求TopN

小马哥

大数据 hadoop mapreduce 七日更

创始人去世,股权怎么办?|视频号28天(21)

赵新龙

28天写作

西少爷肉夹馍的股权纠纷 | 视频号28天(22)

赵新龙

28天写作

个人隐私后续

张老蔫

28天写作

企业级低代码平台的选型和建设思考

李小腾

对机器视觉领域的几点看法

JiangX

机器视觉 28天写作

我的算法学习之路

熊斌

学习方法 算法 28天写作

HTTPS实现原理

架构精进之路

https 七日更 28天写作

扎根CNCF社区贡献五年是怎样的体验?听听华为云原生开源团队的负责人怎么说

华为云开发者联盟

容器 Volcano cncf kubeedge 代码开发

大数据丨ClickHouse在京东能源管理平台的实践

京东科技开发者

数据库 大数据

LeetCode题解:200. 岛屿数量,DFS,JavaScript,详细注释

Lee Chen

算法 大前端 LeetCode

批判性思维自修课(三)

石君

28天写作 批判性思维

折腾CUDA环境,搭建TF2开发环境

IT蜗壳-Tango

七日更

网站自动化任务脚本

Kylin

七日更

低代码:Microsoft Power Platform

lidaobing

低代码 28天写作 Power Platform

凝聚人心并不难,小诀窍让团队跟你一条心

一笑

管理 激励 28天写作

借助Serverless框架构建RESTful API_大前端_Jamie Livingstone_InfoQ精选文章