开始使用 AWS

构建无服务器 Web 应用程序

(利用 AWS Lambda、Amazon API Gateway、AWS Amplify、Amazon DynamoDB 和 Amazon Cognito)

构建无服务器 Web 应用程序

模块 3:无服务器服务后端

您将使用 AWS Lambda 和 Amazon DynamoDB 来构建一个后端进程,用于处理 Web 应用程序的请求。

概览

在本模块中,您将使用 AWS Lambda 和 Amazon DynamoDB 来构建一个后端进程,用于处理 Web 应用程序的请求。用户可以通过您在第一个模块中部署的浏览器应用程序来请求将 Unicorn 发送至他们选择的位置。为了满足这些请求,在浏览器中运行的 JavaScript 需要调用在云中运行的服务。

架构概览

架构概览

您将实施一个用户每次请求获取 Unicorn 时都会调用的 Lambda 函数。该函数将从队列中选择一个 Unicorn,在 DynamoDB 表中记录该请求,然后使用将要分派的 Unicorn 的详细信息响应前端应用程序。

系统会使用 Amazon API Gateway 通过浏览器调用此函数。您将在下一模块中实施该连接。在本模块中,您只需要单独测试函数。

 完成时间

30 分钟

 使用的服务

实施

  • 使用 Amazon DynamoDB 控制台创建一个新的 DynamoDB 表。 

    1. Amazon DynamoDB 控制台中,选择创建表
    2. 输入 Rides 作为表名称。 此字段区分大小写。
    3. 输入 RideId 作为分区键,并选择字符串作为键类型。此字段区分大小写。
    4. 表设置部分中,确保选中默认设置,然后选择创建表。 
    5. 页面上,等待表创建完成。完成后,状态将显示为活动。选择您的表名称
    6. 在新表格的概述选项卡 > 常规信息部分,选择其他信息。复制该 ARN。您在下一节中会用到它。
  • 每个 Lambda 函数都有一个与之关联的 IAM 角色。此角色定义了函数可与哪些 AWS 服务进行交互。在本教程中,您需要创建一个 IAM 角色,用于授予 Lambda 函数将日志写入 Amazon CloudWatch Log 和向 DynamoDB 表中写入项目的权限。

    1. IAM 控制台中,选择左侧导航窗格中的角色,然后选择创建角色
    2. 可信实体类型部分中,选择 AWS 服务。对于用例,选择 Lambda,然后选择下一步。 
      注意:选择角色类型会自动为您的角色创建信任策略,并允许 AWS 服务代表您担任此角色。如果您使用 CLI、AWS CloudFormation 或其他机制创建此角色,请直接指定信任策略。
    3. 在筛选器文本框中输入 AWSLambdaBasicExecutionRole,然后按 Enter。 
    4. 选中 AWSLambdaBasicExecutionRole 策略名称旁边的复选框,然后选择下一步
    5. 角色名称中输入 WildRydesLambda为其他参数保留默认设置。
    6. 选择创建角色
    7. 角色页面的筛选条件框中,键入 WildRydesLambda,然后选择刚才创建的角色的名称。
    8. 权限选项卡的添加权限下,选择创建内联策略
    9. 选择服务部分的搜索栏中输入 DynamoDB,然后在 DynamoDB 出现后将其选中。
    10. 选择 Select actions (选择操作)
    11. 允许的操作部分的搜索栏中键入 PutItem,然后在出现 PutItem 后选中其旁边的复选框。
    12. 资源部分,在选中特定选项的情况下选择添加 ARN 链接。
    13. 选择文本选项卡。粘贴您上一节的步骤 6 中,在 DynamoDB 中创建的表的 ARN,然后选择添加 ARN
    14. 选择下一步
    15. 输入 DynamoDBWriteAccess 作为策略名称,然后选择创建策略
  • AWS Lambda 将运行您的代码以响应诸如 HTTP 请求之类的事件。在此步骤中,您将构建核心函数,用于处理来自 Web 应用程序的 API 请求,以便分派 Unicorn。在下一模块中,您将使用 Amazon API Gateway 创建一个 RESTful API,用于公开可以通过用户浏览器调用的 HTTP 终端节点。然后,您可以将在此步骤中创建的 Lambda 函数连接到此 API,以便为您的 Web 应用程序创建功能齐全的后端。

    使用 AWS Lambda 控制台创建一个名为 RequestUnicorn 的新 Lambda 函数,用于处理 API 请求。将以下 requestUnicorn.js 示例实施用于您的函数代码。只需从该文件中复制该示例并将其粘贴到 AWS Lambda 控制台的编辑器中即可。

    务必将该函数配置为使用您在上一节中创建的 WildRydesLambda IAM 角色。

    1. AWS Lambda 控制台中,选择创建函数
    2. 将默认的 Author from scratch (从头开始创作) 卡片保持选定状态。
    3. 函数名称字段中输入 RequestUnicorn
    4. 选择 Node.js 16.x 作为运行时系统(更新版本的 Node.js 不适用于本教程)。
    5. 更改默认执行角色下拉列表中选择使用现有角色
    6. 现有角色下拉列表中选择 WildRydesLambda
    7. 单击创建函数
    8. 向下滚动到代码源部分,然后将 index.js 代码编辑器中的现有代码替换为 requestUnicorn.js 的内容。以下代码块显示了 requestUnicorn.js 文件。将此代码复制并粘贴到代码编辑器的 index.js 选项卡中。
    const randomBytes = require('crypto').randomBytes;
    const AWS = require('aws-sdk');
    const ddb = new AWS.DynamoDB.DocumentClient();
    
    const fleet = [
        {
            Name: 'Angel',
            Color: 'White',
            Gender: 'Female',
        },
        {
            Name: 'Gil',
            Color: 'White',
            Gender: 'Male',
        },
        {
            Name: 'Rocinante',
            Color: 'Yellow',
            Gender: 'Female',
        },
    ];
    
    exports.handler = (event, context, callback) => {
        if (!event.requestContext.authorizer) {
          errorResponse('Authorization not configured', context.awsRequestId, callback);
          return;
        }
    
        const rideId = toUrlString(randomBytes(16));
        console.log('Received event (', rideId, '): ', event);
    
        // Because we're using a Cognito User Pools authorizer, all of the claims
        // included in the authentication token are provided in the request context.
        // This includes the username as well as other attributes.
        const username = event.requestContext.authorizer.claims['cognito:username'];
    
        // The body field of the event in a proxy integration is a raw string.
        // In order to extract meaningful values, we need to first parse this string
        // into an object. A more robust implementation might inspect the Content-Type
        // header first and use a different parsing strategy based on that value.
        const requestBody = JSON.parse(event.body);
    
        const pickupLocation = requestBody.PickupLocation;
    
        const unicorn = findUnicorn(pickupLocation);
    
        recordRide(rideId, username, unicorn).then(() => {
            // You can use the callback function to provide a return value from your Node.js
            // Lambda functions. The first parameter is used for failed invocations. The
            // second parameter specifies the result data of the invocation.
    
            // Because this Lambda function is called by an API Gateway proxy integration
            // the result object must use the following structure.
            callback(null, {
                statusCode: 201,
                body: JSON.stringify({
                    RideId: rideId,
                    Unicorn: unicorn,
                    Eta: '30 seconds',
                    Rider: username,
                }),
                headers: {
                    'Access-Control-Allow-Origin': '*',
                },
            });
        }).catch((err) => {
            console.error(err);
    
            // If there is an error during processing, catch it and return
            // from the Lambda function successfully. Specify a 500 HTTP status
            // code and provide an error message in the body. This will provide a
            // more meaningful error response to the end client.
            errorResponse(err.message, context.awsRequestId, callback)
        });
    };
    
    // This is where you would implement logic to find the optimal unicorn for
    // this ride (possibly invoking another Lambda function as a microservice.)
    // For simplicity, we'll just pick a unicorn at random.
    function findUnicorn(pickupLocation) {
        console.log('Finding unicorn for ', pickupLocation.Latitude, ', ', pickupLocation.Longitude);
        return fleet[Math.floor(Math.random() * fleet.length)];
    }
    
    function recordRide(rideId, username, unicorn) {
        return ddb.put({
            TableName: 'Rides',
            Item: {
                RideId: rideId,
                User: username,
                Unicorn: unicorn,
                RequestTime: new Date().toISOString(),
            },
        }).promise();
    }
    
    function toUrlString(buffer) {
        return buffer.toString('base64')
            .replace(/\+/g, '-')
            .replace(/\//g, '_')
            .replace(/=/g, '');
    }
    
    function errorResponse(errorMessage, awsRequestId, callback) {
      callback(null, {
        statusCode: 500,
        body: JSON.stringify({
          Error: errorMessage,
          Reference: awsRequestId,
        }),
        headers: {
          'Access-Control-Allow-Origin': '*',
        },
      });
    }

        9.选择部署

  • 在本模块中,您将测试使用 AWS Lambda 控制台构建的函数。在下一模块中,您将使用 API Gateway 添加一个 REST API,以便从在第一个模块中部署的基于浏览器的应用程序中调用此函数。

    1. 在上一节中构建的 RequestUnicorn 函数中,在代码源部分中选择测试,然后从下拉列表中选择配置测试事件
    2. 保留创建新事件的默认选项。
    3. 事件名称 字段中输入 TestRequestEvent
    4. 复制以下测试事件并将其粘贴到 Event JSON 部分:
    {
        "path": "/ride",
        "httpMethod": "POST",
        "headers": {
            "Accept": "*/*",
            "Authorization": "eyJraWQiOiJLTzRVMWZs",
            "content-type": "application/json; charset=UTF-8"
        },
        "queryStringParameters": null,
        "pathParameters": null,
        "requestContext": {
            "authorizer": {
                "claims": {
                    "cognito:username": "the_username"
                }
            }
        },
        "body": "{\"PickupLocation\":{\"Latitude\":47.6174755835663,\"Longitude\":-122.28837066650185}}"
    }

        5.选择 Save(保存)。

        6.在函数的代码源部分中,选择测试,然后从下拉列表中选择 TestRequestEvent

        7.  在测试选项卡上,选择测试

        8.在出现的执行函数:成功消息中,展开详细信息下拉列表。

        9.验证函数结果是否如下所示:

    {
        "statusCode": 201,
        "body": "{\"RideId\":\"SvLnijIAtg6inAFUBRT+Fg==\",\"Unicorn\":{\"Name\":\"Rocinante\",\"Color\":\"Yellow\",\"Gender\":\"Female\"},\"Eta\":\"30 seconds\"}",
        "headers": {
            "Access-Control-Allow-Origin": "*"
        }
    }

此页内容对您是否有帮助?