react 服务器
by Peter Mbanugo
彼得·姆巴努戈(Peter Mbanugo)
Serverless is a cloud-computing execution model in which the cloud provider is responsible for executing a piece of code by dynamically allocating resources to run the code when needed. In a previous post, we looked at what serverless is, and we set up our computer to be able to build serverless applications using AWS Amplify. We bootstrapped a React project and added the Amplify library to it. In this post, we will use the Amplify CLI to provision a secured backend API and a NoSQL database. Then we will consume this API from the React project.
无服务器是一种云计算执行模型,其中云提供商负责通过在需要时动态分配资源来运行代码来执行一段代码。 在上一篇文章中 ,我们研究了什么是无服务器,并且将计算机设置为能够使用AWS Amplify构建无服务器应用程序。 我们引导了一个React项目,并向其中添加了Amplify库。 在本文中,我们将使用Amplify CLI设置安全的后端API和NoSQL数据库。 然后,我们将使用React项目中的该API。
The application we’re going to build will allow users to perform basic CRUD operations. We will use a REST API with a NoSQL database. Follow the instruction below to create the serverless backend.
我们将要构建的应用程序将允许用户执行基本的CRUD操作。 我们将使用带有NoSQL数据库的REST API。 请按照以下说明创建无服务器后端。
Run the command amplify add api
.
运行命令amplify add api
。
You get a prompt to select a service type. Choose REST
and press Enter.
您会提示您选择一种服务类型。 选择REST
,然后按Enter。
It prompts you to enter a name for the current category (the API category). Enter todosApi
and press Enter.
它提示您输入当前类别(API类别)的名称。 输入todosApi
,然后按Enter。
You’re asked for a path. Accept the default items
path by pressing Enter.
您被要求提供路径。 通过按Enter接受默认items
路径。
The next prompt asks for the Lambda source. The serverless REST API works by creating a path on API Gateway and mapping that path to a lambda function. The lambda function contains code to execute when a request is made to the path it’s mapped to. We will create a new lambda. Select the option Create a new Lambda function
and press Enter.
下一个提示询问Lambda来源。 无服务器REST API通过在API网关上创建路径并将该路径映射到lambda函数来工作。 lambda函数包含对映射到的路径发出请求时执行的代码。 我们将创建一个新的lambda。 选择选项Create a new Lambda function
,然后按Enter。
Enter todosLambda
as the name of the resource for the category (function category), and press Enter.
输入todosLambda
作为类别(功能类别)的资源名称,然后按Enter。
You will be asked for the name of the lambda function. Enter todos
and press Enter.
系统将要求您输入lambda函数的名称。 输入todos
,然后按Enter。
You will be asked to choose a template for generating code for this function. Choose the option CRUD function for Amazon DynamoDB table (Integration with Amazon API Gateway and Amazon DynamoDB)
and press Enter. This creates an architecture using API Gateway with Express running in an AWS Lambda function that reads and writes to Amazon DynamoDB.
系统将要求您选择一个模板来生成此功能的代码。 选择CRUD function for Amazon DynamoDB table (Integration with Amazon API Gateway and Amazon DynamoDB)
的选项CRUD function for Amazon DynamoDB table (Integration with Amazon API Gateway and Amazon DynamoDB)
,然后按Enter。 这将使用带有Express的API网关创建架构,该架构运行在读取和写入Amazon DynamoDB的AWS Lambda函数中。
The next prompt asks you to choose a DynanoDB data source. We do not have an existing DynamoDB table so we will choose the Create a new DynamoDB table
option. Press Enter to continue. Now you should see DynamoDB database wizard. It'll ask a series of questions to determine how to create the database.
下一个提示符要求您选择DynanoDB数据源。 我们没有现有的DynamoDB表,因此我们将选择Create a new DynamoDB table
选项。 按Enter继续。 现在,您应该看到DynamoDB数据库向导。 它将提出一系列问题来确定如何创建数据库。
You will be asked to enter the name for this resource. Enter todosTable
and press Enter.
系统将要求您输入此资源的名称。 输入todosTable
,然后按Enter。
The next prompt is for the table name. Enter todos
and press Enter.
下一个提示是表名。 输入todos
,然后按Enter。
You will be asked to add columns to the DynamoDB table. Follow the prompt to create column id
with String
as its type.
系统将要求您将列添加到DynamoDB表中。 按照提示创建类型为String
列id
。
Select id
column when asked for the partition key (primary key) for the table.
当要求输入表的分区键(主键)时,选择id
列。
The next prompt asks if you want to add global secondary indexes to your table. Enter n
and press Enter. You should see the message Successfully added DynamoDb table locally
下一个提示询问您是否要向表中添加全局二级索引。 输入n
并按Enter。 您应该看到消息Successfully added DynamoDb table locally
The next prompt asks Do you want to edit the local lambda function now?. Enter n
and press Enter. You should see the message Successfully added the Lambda function locally
.
下一个提示询问您是否要立即编辑本地lambda函数? 。 输入n
并按Enter。 您应该看到消息已Successfully added the Lambda function locally
。
You get asked if you want to restrict access to the API. Enter y
and press Enter.
系统会询问您是否要限制对API的访问。 输入y
,然后按Enter。
For the next prompt, choose Authenticated and Guest users
and press Enter. This option gives both authorized and guest users access to the REST API.
对于下一个提示,选择Authenticated and Guest users
,然后按Enter。 此选项使授权用户和来宾用户均可访问REST API。
Next, you get asked What kind of access do you want for Authenticated users
. Choose read/write
and press Enter.
接下来,您会被询问What kind of access do you want for Authenticated users
。 选择read/write
,然后按Enter。
Now we get a prompt to choose the kind of access for unauthenticated users (i.e gues users). Choose read
and press Enter. You should get the message Successfully added auth resource locally
. This is because we have chosen to restrict access to the API, and the CLI added the Auth category to the project since we don't have any for the project. At this point, we've added resources that are needed to create our API (API Gateway, DynamoDB, Lambda function, and Cognito for authentication).
现在,我们将提示您选择未经身份验证的用户(即gues用户)的访问类型。 选择read
,然后按Enter。 您应该收到消息“ Successfully added auth resource locally
。 这是因为我们选择限制对API的访问,并且CLI对项目添加了Auth类别,因为我们没有该项目的任何内容。 此时,我们已经添加了创建API所需的资源(API网关,DynamoDB,Lambda函数和用于身份验证的Cognito)。
We get asked if we want to add another path to the API. Enter n
and press Enter. This completes the process and we get the message Successfully added resource todosApi locally
.
我们被询问是否要向该API添加其他路径。 输入n
并按Enter。 这样就完成了整个过程,并且我们收到消息“ Successfully added resource todosApi locally
。
The amplify add api
command took us through the process of creating a REST API. This API will be created based on the options we chose. To create this API requires 4 AWS services. They're:
amplify add api
命令引导我们完成了创建REST API的过程。 该API将根据我们选择的选项创建。 要创建此API,需要4个AWS服务。 他们是:
Amazon DynamoDB. This will serve as our NoSQL database. We created a DynomoDB table named todos
when we added the todosTable
resource. We gave it 3 columns with id
as the primary key.
Amazon DynamoDB。 这将用作我们的NoSQL数据库。 添加todosTable
资源时,我们创建了一个名为todos
的DynomoDB表。 我们给它3列,其中id
为主键。
Amazon API Gateway. This is what allows us to create REST API endpoint. We added a resource for this named todosApi
, with a path items
. We also selected the option to restrict access to the API.
Amazon API网关。 这就是允许我们创建REST API端点的原因。 我们为此资源添加了一个名为todosApi
的资源,其中包含一个路径items
。 我们还选择了限制访问API的选项。
However, the service specifications for these services are not yet in the cloud. We need to update the project in the cloud with information to provide the needed services. Run the command amplify status
, and we should get a table with information about the amplify project.
但是,这些服务的服务规范尚未在云中。 我们需要使用信息更新云中的项目以提供所需的服务。 运行命令amplify status
,我们应该得到一个表,其中包含有关amplify项目的信息。
Open the file backend/function/todosLambda/src/app.js. You will notice that this file contains code generated during the resource set up process. It uses Express.js to set up routes, and aws-serverless-express package to easily build RESTful APIs using the Express.js framework on top of AWS Lambda and Amazon API Gateway.
打开文件backend / function / todosLambda / src / app.js。 您会注意到该文件包含在资源设置过程中生成的代码。 它使用Express.js设置路由,并使用aws-serverless-express包使用AWS Lambda和Amazon API Gateway之上的Express.js框架轻松构建RESTful API。
When we push the project configuration to the cloud, it’ll configure a simple proxy API using Amazon API Gateway and integrate it with this Lambda function. The package includes middleware to easily get the event object Lambda receives from API Gateway. It was applied on line 32 app.use(awsServerlessExpressMiddleware.eventContext());
and used across the routes with codes that looks like req.apiGateway.event.*
. The pre-defined routes allow us to perform CRUD operation on the DynamoDB table.
当我们将项目配置推送到云中时,它将使用Amazon API Gateway配置一个简单的代理API,并将其与此Lambda函数集成。 该软件包包括中间件,可轻松获取Lambda从API Gateway接收的事件对象。 它应用于第32行app.use(awsServerlessExpressMiddleware.eventContext());
并在路由中使用类似于req.apiGateway.event.*
代码。 预定义的路由使我们可以在DynamoDB表上执行CRUD操作。
We will make a couple of changes to this file. The first will be to change the value for tableName
variable from todosTable
to todos
. When creating the DynamoDB resource, we specified todosTable
as the resource name and todos
as the table name, so it wrongly used the resource name as the table name when the file was created. This would likely be fixed in a future version of the CLI, so if you don't find it wrongly used, you can skip this step. We will also need to update the definitions.
我们将对该文件进行一些更改。 首先是将tableName
变量的值从todosTable
为todos
。 当创建DynamoDB资源,我们指定todosTable
作为资源名称和todos
表名,所以它错误地使用资源名称作为表名创建文件时。 将来的CLI版本中可能会修复此问题,因此,如果没有发现使用不当,则可以跳过此步骤。 我们还需要更新定义。
Change the first route definition to use the code below.
更改第一个路线定义以使用下面的代码。
app.get(path, function(req, res) { const queryParams = { TableName: tableName, ProjectionExpression: "id, title" };
dynamodb.scan(queryParams, (err, data) => { if (err) { res.json({ error: "Could not load items: " + err }); } else { res.json(data.Items); } });});
This defines a route to respond to the /items path with code to return all data in the DynamoDB table. The ProjectionExpression
values are used to specify that it should get only the columns id
and title
.
这定义了一条路由,该路由使用代码返回/ ynms路径以返回DynamoDB表中的所有数据。 ProjectionExpression
值用于指定应仅获取id
和title
列。
Change the route definition on line 77 to read as app.get(path + hashKeyPath + sortKeyPath, function(req, res) {
. This allows us to retrieve an item by its id
following the path /items/:id. Also, change line 173 to be app.delete(path + hashKeyPath + sortKeyPath, function(req, res) {
. This responds to HTTP DELETE method to delete an item following the path /items/:id.
改变线77路线定义改为app.get(path + hashKeyPath + sortKeyPath, function(req, res) {
。这使得我们可以检索其项id
下面的路径/项目/:ID此外,变化第173行是app.delete(path + hashKeyPath + sortKeyPath, function(req, res) {
。这响应HTTP DELETE方法删除路径/ items /:id之后的项目 。
The AWS resources have been added and updated locally, and we need to provision them in the cloud. Open the command line and run amplify push
. You'll get a prompt if you want to continue executing the command. Enter y
and press Enter. What this does is that it'll upload the latest versions of the resources nested stack templates to an S3 deployment bucket, and then call the AWS CloudFormation API to create / update resources in the cloud.
AWS资源已在本地添加和更新,我们需要在云中进行配置。 打开命令行并运行amplify push
。 如果要继续执行该命令,将提示您。 输入y
,然后按Enter。 这样做是将最新版本的资源嵌套堆栈模板上载到S3部署存储桶,然后调用AWS CloudFormation API在云中创建/更新资源。
When the amplify push
command completes, you'll see a file aws-exports.js in the src folder. This file contains information to the resources that were created in the cloud. Each time a resource is created or updated by running the push
command, this file will be updated. It's created for JavaScript projects and will be used in Amplify JavaScript library. We will be using this in our React project. We will also use Bootstrap to style the page. Open public/index.html and add the following in the head:
当amplify push
命令完成时,您将在src文件夹中看到文件aws-exports.js 。 该文件包含有关在云中创建的资源的信息。 每次通过运行push
命令创建或更新资源时,都会更新此文件。 它是为JavaScript项目创建的,将在Amplify JavaScript库中使用。 我们将在我们的React项目中使用它。 我们还将使用Bootstrap设置页面样式。 打开public / index.html并在标题中添加以下内容:
<link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.1.3/css/bootstrap.min.css" integrity="sha384-MCw98/SFnGE8fJT3GXwEOngsV7Zt27NXFoaoApmYm81iuXoPkFOJwJ8ERdknLPMO" crossorigin="anonymous"/><script src="https://code.jquery.com/jquery-3.3.1.slim.min.js" integrity="sha384-q8i/X+965DzO0rT7abK41JStQIAqVgRVzpbzo5smXKp4YfRvH+8abtTE1Pi6jizo" crossorigin="anonymous"></script><script src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.14.3/umd/popper.min.js" integrity="sha384-ZMP7rVo3mIykV+2+9J3UJ46jBk0WLaUAdn689aCwoqbBJiSnjAK/l8WvCWPIPm49" crossorigin="anonymous"></script><script src="https://stackpath.bootstrapcdn.com/bootstrap/4.1.3/js/bootstrap.min.js" integrity="sha384-ChfqqxuZUCnJSK3+MXmPNIyE6ZbWh2IMqE241rYiqJxyMiZ6OW/JmZQ5stwEULTy" crossorigin="anonymous"></script>
Add a new file src/List.js with the following content:
添加具有以下内容的新文件src / List.js :
import React from "react";
export default props => ( <div> <legend>List</legend> <div className="card" style={{ width: "25rem" }}> {renderListItem(props.list, props.loadDetailsPage)} </div> </div>);
function renderListItem(list, loadDetailsPage) { const listItems = list.map(item => ( <li key={item.id} className="list-group-item" onClick={() => loadDetailsPage(item.id)} > {item.title} </li> ));
return <ul className="list-group list-group-flush">{listItems}</ul>;}
This component will render a list of items from the API. Add a new file src/Details.js with the following content:
该组件将呈现API中的项目列表。 添加具有以下内容的新文件src / Details.js :
import React from "react";
export default props => ( <div> <h2>Details</h2> <div className="btn-group" role="group"> <button type="button" className="btn btn-secondary" onClick={props.loadListPage} > Back to List </button> <button type="button" className="btn btn-danger" onClick={() => props.delete(props.item.id)} > Delete </button> </div> <legend>{props.item.title}</legend> <div className="card"> <div className="card-body">{props.item.content}</div> </div> </div>);
This component will display the details of an item with buttons to delete that item or go back to the list view. Open src/App.js and update it with this code:
该组件将显示带有按钮的项目详细信息,以删除该项目或返回列表视图。 打开src / App.js并使用以下代码进行更新:
import React, { Component } from "react";import List from "./List";import Details from "./Details";
import Amplify, { API } from "aws-amplify";import aws_exports from "./aws-exports";import { withAuthenticator } from "aws-amplify-react";Amplify.configure(aws_exports);
class App extends Component { constructor(props) { super(props); this.state = { content: "", title: "", list: [], item: {}, showDetails: false }; }
async componentDidMount() { await this.fetchList(); } handleChange = event => { const id = event.target.id; this.setState({ [id]: event.target.value }); };
handleSubmit = async event => { event.preventDefault(); await API.post("todosApi", "/items", { body: { id: Date.now().toString(), title: this.state.title, content: this.state.content } });
this.setState({ content: "", title: "" }); this.fetchList(); }; async fetchList() { const response = await API.get("todosApi", "/items"); this.setState({ list: [...response] }); }
loadDetailsPage = async id => { const response = await API.get("todosApi", "/items/" + id); this.setState({ item: { ...response }, showDetails: true }); };
loadListPage = () => { this.setState({ showDetails: false }); };
delete = async id => { //TODO: Implement functionality };
render() { return ( <div className="container"> <form onSubmit={this.handleSubmit}> <legend>Add</legend> <div className="form-group"> <label htmlFor="title">Title</label> <input type="text" className="form-control" id="title" placeholder="Title" value={this.state.title} onChange={this.handleChange} /> </div> <div className="form-group"> <label htmlFor="content">Content</label> <textarea className="form-control" id="content" placeholder="Content" value={this.state.content} onChange={this.handleChange} /> </div> <button type="submit" className="btn btn-primary"> Submit </button> </form> <hr /> {this.state.showDetails ? ( <Details item={this.state.item} loadListPage={this.loadListPage} delete={this.delete} /> ) : ( <List list={this.state.list} loadDetailsPage={this.loadDetailsPage} /> )} </div> ); }}
export default withAuthenticator(App, true);
We imported the Amplify library and initialized it by calling Amplify.configure(aws_exports);
. When the component is mounted, we call fetchList()
to retrieve items from the API.
我们导入了Amplify库,并通过调用Amplify.configure(aws_exports);
对其进行了初始化Amplify.configure(aws_exports);
。 装入组件后,我们调用fetchList()
从API检索项目。
This function uses the API client from the Amplify library to call the REST API. Under the hood, it utilizes Axios to execute the HTTP requests. It'll add necessary headers to the request so you can successfully call the REST API. You can add headers if you defined custom headers for your API.
此函数使用Amplify库中的API客户端来调用REST API。 在后台,它利用Axios执行HTTP请求。 它将为请求添加必要的标头,以便您可以成功调用REST API。 如果您为API定义了自定义标头,则可以添加标头。
For our project, we only specify the apiName and path when invoking the functions from the API client. The loadDetailsPage()
function fetches a particular item from the database through the API. Then sets item
state with the response and showDetails
to true. This showDetails
is used in the render function to toggle between showing a list of items or the details page of a selected item. The function handleSubmit()
is called when the form is submitted. It sends the form data to the API to create a document in the database, with columns id
, title
and content
, then calls fetchList()
to update the list. I left the delete()
function empty so you can implement it yourself. What better way to learn than to try it yourself ?.
对于我们的项目,我们仅在从API客户端调用函数时指定apiName和路径。 loadDetailsPage()
函数通过API从数据库中获取特定项。 然后使用响应设置item
状态,并将showDetails
为true。 此showDetails
在渲染功能中用于在显示项目列表或所选项目的详细信息页面之间切换。 提交表单时会调用handleSubmit()
函数。 它将表单数据发送到API,以在数据库中创建具有id
, title
和content
列的文档,然后调用fetchList()
来更新列表。 我将delete()
函数留空,以便您可以自己实现。 有什么比自己尝试更好的学习方法?
This function will be called from the delete button in the Details
component. The code you have in it should call the API to delete an item by id
and display the list component with correct items.
该函数将从“ Details
组件中的“删除”按钮调用。 其中包含的代码应调用API以按id
删除项目,并显示包含正确项目的列表组件。
We wrapped the App component with the withAuthenticator
higher order component from the Amplify React library. This provides the app with complete flows for user registration, sign-in, signup, and sign out. Only signed in users can access the app since we're using this higher order component. The withAuthenticator
component automatically detects the authentication state and updates the UI. If the user is signed in, the underlying App component is displayed, otherwise, sign-in/signup controls are displayed.
我们使用来自Amplify React库的withAuthenticator
高阶组件包装了App组件。 这为应用程序提供了用于用户注册,登录,注册和注销的完整流程。 由于我们使用的是高级订单组件,因此只有登录用户才能访问该应用。 withAuthenticator
组件自动检测身份验证状态并更新UI。 如果用户已登录,则显示基础的App组件,否则,将显示登录/注册控件。
The second argument which was set to true
tells it to display a sign-out button at the top of the page. Using the withAuthenticator
component is the simplest way to add authentication flows into your app. You can also have a custom UI and use set of APIs from the Amplify library to implement sign-in and sign up flows. See the docs for more details.
设置为true
的第二个参数告诉它在页面顶部显示一个注销按钮。 使用withAuthenticator
组件是将身份验证流添加到您的应用程序的最简单方法。 您还可以具有自定义UI,并使用Amplify库中的API集来实现登录和注册流程。 有关更多详细信息,请参阅文档 。
We have all the code necessary to use the application. Open the terminal and run npm start
to start the application. You'll need to signup and sign in to use the application.
我们拥有使用该应用程序所需的所有代码。 打开终端并运行npm start
以启动应用程序。 您需要注册并登录才能使用该应用程序。
We went through creating our backend services using the Amplify CLI. The command amplify add api
took us through adding resources for DynamoDB, Lambda, API Gateway, and Cognito for authentication. We updated the code in backend/function/todosLambda/src/app.js to match our API requirement. We added UI components to perform CRUD operations on the app and used a higher order component from the Amplify React library to allow only authenticated users access to the application.
我们使用Amplify CLI创建了后端服务。 命令amplify add api
使我们通过添加DynamoDB,Lambda,API Gateway和Cognito的资源进行身份验证。 我们更新了backend / function / todosLambda / src / app.js中的代码以匹配我们的API要求。 我们添加了UI组件以在应用程序上执行CRUD操作,并使用了Amplify React库中的高阶组件,以仅允许经过身份验证的用户访问应用程序。
You should notice we only used a few lines of code to add authentication flows and call the API. Also creating the serverless backend services and connecting them all together was done with a command and responding to the prompts that followed. Thus showing how AWS Amplify makes development easier.
您应该注意到,我们仅使用了几行代码来添加身份验证流程并调用API。 还创建无服务器后端服务并将它们连接在一起,这是通过命令完成的,并响应随后的提示。 因此,展示了AWS Amplify如何使开发更加容易。
Originally posted on my blog.
最初发布在我的博客上 。
react 服务器