在这篇文章中,我将会分享我的经验,讲解如何使用“Serverless”,并且使用AWS Lambda、API Gateway和DynamoDB搭建一个CRUD API微服务。
假设你已经有了AWS账户,并且已经安装了NodeJS。
下一步,你需要安装Serverless npm package,它能够让你轻松的创建、编辑和部署微服务:
npm install -g serverless
之后按照亚马逊的指导,创建一个IAM用户,然后使用那些证书配置Serverless。
指定新项目的储存路径,然后运行:
serverless create --template aws-nodejs --path pets-service
现在我们要在项目中审定linting。这篇文章并不是为了介绍ESLint,所以我不会说的太详细,但是我建议你现在安装,并且用下面的方式设定.eslintrc:
{
“plugins”: [“node”],
“extends”: [“eslint:recommended”, “plugin:node/recommended”],
“env”: {
“node”: true,
“mocha”: true
},
“rules”: {
“no-console”: 0,
“node/no-unsupported-features”: [2, {“version”: 4}]
}
}
使用npm安装aws-sdk和lodash:
npm i -S aws-sdk lodash
转到handler.js,在文件顶部添加依赖:
const aws = require(‘aws-sdk’);
const _ = require(‘lodash/fp’);
请注意,我们使用的是Iodash的“functional programming”变体,因为它的合并功能不会改变原始对象。
下面,使用DynamoDB设定用于沟通的文件客户端:
const dynamo = new aws.DynamoDB.DocumentClient();
现在来创建create()函数,目的是在数据库中建立一个新的Pet:
exports.create = function(event, context) {
const payload = {
TableName: 'Pets',
Item: event.body
};
const cb = (err, data) => {
if (err) {
console.log(err);
context.fail(‘Error creating pet’);
} else {
console.log(data);
context.succeed(data);
}
}
dynamo.put(payload, cb);
};
这段代码其实并不难理解:
我们获得了一个事件对象,对象中有一个key body,里面是我们想要储存的数据。
我们还提供了一个callback,它有两个重要的目的:
如果出现错误,我们运行context.fail(),它基本上就是一个AWS提供的onError callback。
如果项目创建成功,我们则运行context.succeed(),数据中的传递会被作为Lambda函数的结果被返回。
DynamoDB有一个要求,那就是在创建的时候,我们必须要提供主要的key。在这个例子中,我们必须要在event.body对象中包含petId,将它作为key。
在CRUD的操作上,我们也会使用相同的方式:
exports.show = function(event, context) {
const payload = {
TableName: 'Pets',
Key: {
petId: event.params.path.petId
}
}
const cb = (err, data) => {
if (err) {
console.log(err);
context.fail('Error retrieving pet');
} else {
console.log(data);
context.done(null, data);
}
}
dynamo.get(payload, cb);
};
exports.list = function(event, context) {
const payload = {
TableName: 'Pets'
}
const cb = (err, data) => {
if (err) {
console.log(err);
context.fail('Error getting pets');
} else {
console.log(data);
context.done(null, data);
}
}
dynamo.scan(payload, cb);
}
exports.update = function(event, context) {
const payload = {
TableName: 'Pets',
Key: {
petId: event.params.path.petId
}
};
dynamo.get(payload, (err, data) => {
if (err) {
console.log(err);
context.fail('No pet with that id exists.');
} else {
const item = _.merge(data.Item, event.body);
payload.Item = item;
dynamo.put(payload, (putErr, putData) => {
if (putErr) {
console.log('Error updating pet.');
console.log(putErr);
context.fail('Error updating pet.');
} else {
console.log('Success!');
console.log(putData);
context.done(null, item);
}
});
}
});
}
exports.delete = function(event, context) {
const payload = {
TableName: 'Pets',
Key: {
petId: event.params.path.petId
}
};
const cb = (err, data) => {
if (err) {
console.log(err);
context.fail('Error retrieving pet');
} else {
console.log(data);
context.done(null, data);
}
}
dynamo.delete(payload, cb);
}
有两点需要指出的是:
我们需要获得部分省级的能力,也就是说你不需要随变更一起发送整个Pet对象,只需要发送变更就好。为了实现这个目的,我们需要在update()函数中先调用一个get,然后将变更合并进这次操作的结果中。
petId被当做一个parameter被传递进了API Gateway,之后为通过event.params.path.petId出现在Lambda中。如果你喜欢的话,你也可以使用查询字符串。
马上就要完成了,我们现在就来搞定Serverless配置文件。打开serverless.yml,然后按照下面的方式对其进行编辑:
service: pets-service
provider:
name: aws
runtime: nodejs4.3
defaults:
stage: dev
region: us-west-2
functions:
createPet:
handler: handler.create
events:
- http:
path: pets
method: POST
showPet:
handler: handler.show
events:
- http:
path: pets/{petId}
method: GET
listPets:
handler: handler.list
events:
- http:
path: pets
method: GET
updatePet:
handler: handler.update
events:
- http:
path: pets/{petId}
method: PUT
deletePet:
handler: handler.delete
events:
- http:
path: pets/{petId}
method: DELETE
这个配置很好理解。首先指定Lambda函数的名称,然后将他们map到handler.js函数,以及所需的HTTP方法和路径上。
我将默认的region更改成了 ‘us-west-2’,而stage还是默认的’dev’。
API Gateways的名称中不应该提及环境信息,因为它可以处理多种环境或是“stage”。
现在开始部署了。要做的只有运行下面这条命令:
serverless deploy
大概一分钟左右,你的Lambda就应该部署好了,API Gateway也创建好了。
创建一个名为‘Pets’的DynamoDB table,找到 ‘dev-pets-service’,然后将其导航到POST方法。
点击TEST,对API进行测试,你需要下面这些数据:
{ petId: "029340", name: "Fido", type: "dog" }
现在你应该已经在数据库中成功创建了一个新的项目。
你下一步或许想要为你的资源打开CORS,给你的API使用自定义域名,以及对你的前端应用进行设定,让它们与这些endpoint进行对话。
这些操作都不在本文的讨论范围内,幸运的是这些操作都不难。
Reddit用户jcready提出了一个意见,可以改进方法的升级:
exports.update = function(event, context) {
const payload = _.reduce(event.body, (memo, value, key) => {
memo.ExpressionAttributeNames[`#${key}`] = key
memo.ExpressionAttributeValues[`:${key}`] = value
memo.UpdateExpression.push(`#${key} = :${key}`)
return memo
}, {
TableName: 'Pets',
Key: { petId: event.params.path.petId },
UpdateExpression: [],
ExpressionAttributeNames: {},
ExpressionAttributeValues: {}
})
payload.UpdateExpression = 'SET ' + payload.UpdateExpression.join(', ')
dynamo.update(payload, context.done)
}
我们当前的部署方法存在一个问题,那就是如果两个更新请求同时被发送,一个用户可以覆盖另一个用户的变更。
DocumentClient提供了一个更新方法,让我们可以声明需要升级哪个field,但是它的语法有一点古怪,而且需要生成一个“UpdateExpression”才能实现这些变更。上面的这段代码可以解决这个问题。
原文来自:SDK.cn
声明:所有来源为“聚合数据”的内容信息,未经本网许可,不得转载!如对内容有异议或投诉,请与我们联系。邮箱:marketing@think-land.com
涉农贷款地址识别,支持对私和对公两种方式。输入地址的行政区划越完整,识别准确度越高。
根据给定的手机号、姓名、身份证、人像图片核验是否一致
通过企业关键词查询企业涉讼详情,如裁判文书、开庭公告、执行公告、失信公告、案件流程等等。
IP反查域名是通过IP查询相关联的域名信息的功能,它提供IP地址历史上绑定过的域名信息。
结合权威身份认证的精准人脸风险查询服务,提升人脸应用及身份认证生态的安全性。人脸风险情报库,覆盖范围广、准确性高,数据权威可靠。