你在使用哪种编程语言?快来投票,亲手选出你心目中的编程语言之王 了解详情
写点什么

借助 Serverless 框架构建 RESTful API

2019 年 7 月 03 日

借助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 年 7 月 03 日 19:335621
用户头像

发布了 371 篇内容, 共 161.7 次阅读, 收获喜欢 838 次。

关注

评论

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

28岁程序员期权过亿,彪悍从字节退休,网友:酸了酸了!

程序员生活志

程序员 字节跳动 开发 退休

看清远处模糊的事,不如做好身边清楚的事

Neco.W

创业心态 未知

红警1游戏开源,代码非常规范。网友:秀色可餐

程序员生活志

游戏开源 红警1

当你启动Redis的时候,Redis做了什么

老胡爱分享

redis 源码分析 面试题

面向开发者的 WSL2 安装指南

simpleapples

Python golang Windows 10 wsl

免费下载 | 阿里云实时计算整体解决方案白皮书重磅发布!

Apache Flink

大数据 flink 流计算 实时计算 大数据处理

架构训练营第二章作业

mh

设计模式的主要原则

依赖倒置架构

AIK

第二周作业

晨光

第二周总结

晨光

极客大学架构师训练营 框架设计、设计原则、设计模式 第四课 听课总结

John(易筋)

极客时间 极客大学 极客大学架构师训练营 设计原则 框架设计

使用WebMaker快速预览Ionic页面效果

davidce

Ionic WebMaker 混合应用开发

【大厂面试06期】谈一谈你对Redis持久化的理解?

NotFound9

数据库 redis 后端

【第二周学习总结】

黑莓

依赖倒置及 Cache 重构设计

秤须苑

极客大学架构师训练营

Mybatis-plus 之 DIP

无心水

极客大学架构师训练营

课程总结

AIK

第二周总结

qqq

极客大学架构师训练营

20年行业变革与技术演进,当下CDN如何为政企数字化转型加速?

阿里云Edge Plus

CDN 边缘计算 移动视频

如何构建低延时的直播体验,让互动更实时?

阿里云Edge Plus

CDN 短视频 直播 视频

【架构】—回归本质(面向对象)

不二架构

面向对象 架构师 极客大学架构师训练营

小师妹学JVM之:JDK14中JVM的性能优化

程序那些事

JVM 「Java 25周年」 小师妹 JIT JDK14

第二周作业

Aldaron

【Week02】框架设计

Aldaron

编程这件事

dapaul

第二周 作业

尔东雨田

架构师训练营 0 期第二周

Blink

设计原则之依赖倒置和接口隔离

dapaul

数仓系列 | 深入解读 Flink 资源管理机制

Apache Flink

大数据 flink 流计算 实时计算

架构师训练营第二章 总结

尔东雨田

技术为帆,纵横四海- Lazada技术东南亚探索和成长之旅

技术为帆,纵横四海- Lazada技术东南亚探索和成长之旅

借助Serverless框架构建RESTful API-InfoQ