<?xml version="1.0" encoding="UTF-8"?>
<rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/"
    xmlns:atom="http://www.w3.org/2005/Atom" xmlns:media="http://search.yahoo.com/mrss/" version="2.0">
    <channel>
        
        <title>
            <![CDATA[ AWS - freeCodeCamp.org ]]>
        </title>
        <description>
            <![CDATA[ freeCodeCamp 是一个免费学习编程的开发者社区，涵盖 Python、HTML、CSS、React、Vue、BootStrap、JSON 教程等，还有活跃的技术论坛和丰富的社区活动，在你学习编程和找工作时为你提供建议和帮助。 ]]>
        </description>
        <link>https://www.freecodecamp.org/chinese/news/</link>
        <image>
            <url>https://cdn.freecodecamp.org/universal/favicons/favicon.png</url>
            <title>
                <![CDATA[ AWS - freeCodeCamp.org ]]>
            </title>
            <link>https://www.freecodecamp.org/chinese/news/</link>
        </image>
        <generator>Eleventy</generator>
        <lastBuildDate>Sat, 30 May 2026 08:36:29 +0000</lastBuildDate>
        <atom:link href="https://www.freecodecamp.org/chinese/news/tag/aws/rss.xml" rel="self" type="application/rss+xml" />
        <ttl>60</ttl>
        
            <item>
                <title>
                    <![CDATA[ 如何使用 AWS API Gateway 向用户提供自定义 API ]]>
                </title>
                <description>
                    <![CDATA[ 在云计算领域和 serverless 架构中，AWS API Gateway 是一款强大的工具，能帮助您搭建强大、安全且可拓展的 API。 在本教程中，首先我将介绍 API 网关是什么，并解释使用 API 网关的好处。接下来，我将展示如何创建、部署一个 Rest API, 并创建使用计划以提供 API 密钥。那么，我们现在就开始吧！ 什么是 API 网关？ AWS API Gateway 是 Amazon Web Services (AWS) 提供的一项全托管服务，可帮助您轻松搭建、部署和管理任意规模的 API。 它充当应用程序的前门，允许您创建充当客户端和后端服务之间桥梁的 API，以便实现安全有效的通信。 为什么需要 API 网关？ AWS API Gateway 可为企业和开发者提供诸多好处，下方列出了一些使用 API 网关的好处。 可拓展性和高可用性 借助 AWS API Gateway，您可以更轻松地进行 API 拓展。通过底层基础设施自动拓展， AWS API ]]>
                </description>
                <link>https://www.freecodecamp.org/chinese/news/how-to-offer-custom-apis-to-your-users-aws-api-gateway/</link>
                <guid isPermaLink="false">64af9123486c7406702c8ed7</guid>
                
                    <category>
                        <![CDATA[ 云计算 ]]>
                    </category>
                
                    <category>
                        <![CDATA[ AWS ]]>
                    </category>
                
                    <category>
                        <![CDATA[ API ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Rhea Xiao ]]>
                </dc:creator>
                <pubDate>Thu, 13 Jul 2023 06:13:58 +0000</pubDate>
                <media:content url="https://chinese.freecodecamp.org/news/content/images/2023/07/AWS-API-Gateway-Banner-3.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <p data-test-label="translation-intro">
        <strong>原文：</strong> <a href="https://www.freecodecamp.org/news/how-to-offer-custom-apis-to-your-users-aws-api-gateway/" target="_blank" rel="noopener noreferrer" data-test-label="original-article-link">How to Offer Custom APIs to Your Users with AWS API Gateway</a>
      </p><!--kg-card-begin: markdown--><p>在云计算领域和 serverless 架构中，AWS API Gateway 是一款强大的工具，能帮助您搭建强大、安全且可拓展的 API。</p>
<p>在本教程中，首先我将介绍 API 网关是什么，并解释使用 API 网关的好处。接下来，我将展示如何创建、部署一个 Rest API, 并创建使用计划以提供 API 密钥。那么，我们现在就开始吧！</p>
<h2 id="api">什么是 API 网关？</h2>
<p>AWS API Gateway 是 Amazon Web Services (AWS) 提供的一项全托管服务，可帮助您轻松搭建、部署和管理任意规模的 API。</p>
<p>它充当应用程序的前门，允许您创建充当客户端和后端服务之间桥梁的 API，以便实现安全有效的通信。</p>
<h2 id="api">为什么需要 API 网关？</h2>
<p>AWS API Gateway 可为企业和开发者提供诸多好处，下方列出了一些使用 API 网关的好处。</p>
<h3 id="">可拓展性和高可用性</h3>
<p>借助 AWS API Gateway，您可以更轻松地进行 API 拓展。通过底层基础设施自动拓展， AWS API Gateway 可以无缝处理流量高峰，以确保高可用性并避免服务中断。</p>
<h3 id="">安全与认证</h3>
<p>API 网关提供强大的安全功能，包括内置的身份验证和授权机制。</p>
<p>它支持通过 IAM 角色对内部应用程序进行用户身份验证，通过 Cognito 对外部应用程序进行身份验证，并且支持自定义授权者。</p>
<h3 id="aws">与其他 AWS 服务集成</h3>
<p>作为 AWS 生态系统的一部分， API 网关与一系列其他 AWS 服务无缝集成，这意味着您能利用 AWS Lambda 函数、用于用户管理的 AWS Cognito，以及用于监管和日志记录的 AWS CloudWatch 等其他功能。</p>
<h3 id="api">API 生命周期管理</h3>
<p>利用 API 网关，您能轻松对不同阶段的 API 进行版本控制、部署及管理。这简化了发布更新、测试新功能以及管理不同环境（比如开发、预生产和生产）的过程。</p>
<p>我希望现在您已经了解了 API 网关是什么以及它为何如此重要。接下来让我们一起来创建自己的 API 网关吧！</p>
<h2 id="awsapigateway">如何创建 AWS API Gateway</h2>
<p>在本节中，我们将：</p>
<ul>
<li>采用 GET 方法创建 Rest API</li>
<li>将其与简单的 hello world lambda 函数集成并进行部署</li>
</ul>
<p>让我们从创建 lambda 函数开始吧</p>
<h2 id="awslambda">如何创建 AWS Lambda 函数</h2>
<p>登录 AWS Management <a href="https://console.aws.amazon.com/">控制台</a> 并在控制台搜索栏中搜索 "Lambda"。然后，单击 Create Function 按钮。</p>
<figure class="kg-card kg-card-image kg-card-hascaption">
    <img src="https://www.freecodecamp.org/news/content/images/2023/06/image-145.png" alt="导航至 AWS Lambda 控制台" class="kg-image" width="600" height="400" loading="lazy">
    <figcaption>导航至 AWS Lambda 控制台</figcaption>
</figure>
<p>选择 "Author from scratch" 选项，输入 lambda 函数名称，选择 "Python" Runtime，然后单击右下方的 Create Function 按钮。</p>
<figure class="kg-card kg-card-image kg-card-hascaption">
    <img src="https://www.freecodecamp.org/news/content/images/2023/06/image-146.png" alt="创建一个 AWS Lambda Function" class="kg-image" width="600" height="400" loading="lazy">
    <figcaption>创建一个 AWS Lambda Function</figcaption>
</figure>
<p>函数创建成功后，请更新下方代码并部署更改：</p>
<pre><code class="language-Python">import json

def lambda_handler(event, context):
    body = "Hello from 5minslearn!"
    statusCode = 200
    return {
        "statusCode": statusCode,
        "body": json.dumps(body),
        "headers": {
            "Content-Type": "application/json"
        }
    }
</code></pre>
<figure class="kg-card kg-card-image kg-card-hascaption">
    <img src="https://www.freecodecamp.org/news/content/images/2023/06/image-147.png" alt="部署 Lambda Function" class="kg-image" width="600" height="400" loading="lazy">
    <figcaption>部署 Lambda Function</figcaption>
</figure>
<p>恭喜! 您已成功创建 AWS Lambda 函数，接下来让我们来创建 Rest API。</p>
<h2 id="restapiawslambda">如何创建 Rest API 并将其与 AWS Lambda 集成</h2>
<p>在搜索栏搜索 API Gateway，然后在 REST API 版块中单击 Build 按钮。</p>
<figure class="kg-card kg-card-image kg-card-hascaption">
    <img src="https://www.freecodecamp.org/news/content/images/2023/06/image-183.png" alt="创建 Rest API" class="kg-image" width="600" height="400" loading="lazy">
    <figcaption>创建 Rest API</figcaption>
</figure>
<p>选择协议为 Rest，并在 Create new API 选项中选择 New API。在设置选项中输入您选择的 API 名称，并保留 Endpoint Type 的默认选项。然后，单击 Create API 按钮。</p>
<figure class="kg-card kg-card-image kg-card-hascaption">
    <img src="https://www.freecodecamp.org/news/content/images/2023/06/image-148.png" alt="配置创建 Rest API" class="kg-image" width="600" height="400" loading="lazy">
    <figcaption>配置创建 Rest API</figcaption>
</figure>
<p>首先单击左上方的 Actions 按钮，然后单击 Method 并选择 GET 方法，再单击勾选图标。</p>
<figure class="kg-card kg-card-image kg-card-hascaption">
    <img src="https://www.freecodecamp.org/news/content/images/2023/06/image-149.png" alt="创建 Method" class="kg-image" width="600" height="400" loading="lazy">
    <figcaption>创建 Method</figcaption>
</figure>
<figure class="kg-card kg-card-image kg-card-hascaption">
    <img src="https://www.freecodecamp.org/news/content/images/2023/06/image-150.png" alt="选择 " get"="" 方法"="" class="kg-image" width="600" height="400" loading="lazy">
    <figcaption>选择 "GET" 方法</figcaption>
</figure>
<p>选择 Lambda Function 作为 Integration type，并输入已创建的 Lambda 函数名称。然后，保存此函数。</p>
<figure class="kg-card kg-card-image kg-card-hascaption">
    <img src="https://www.freecodecamp.org/news/content/images/2023/06/image-151.png" alt="选择 Method 配置" class="kg-image" width="600" height="400" loading="lazy">
    <figcaption>选择 Method 配置</figcaption>
</figure>
<p>单击保存后， 屏幕中将弹出 "Add Permission to Lambda Function"消息提示确认，这就意味着您将允许 API Gateway 调用 Lambda 函数（在本例中指的就是 "DemoFunction" Lambda 函数）。请同意确认，并继续下一步。</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2023/06/image-152.png" alt="image-152" width="600" height="400" loading="lazy"></p>
<p>同意授权通过 API 网关调用 Lambda Function</p>
<p>单击 Test，您将来到一个新页面。单击 "Test" 按钮，此时您能在右侧面板上看到 Lambda 函数做出的响应。</p>
<figure class="kg-card kg-card-image kg-card-hascaption">
    <img src="https://www.freecodecamp.org/news/content/images/2023/06/image-153.png" alt="我们的 API 架构" class="kg-image" width="600" height="400" loading="lazy">
    <figcaption>我们的 API 架构</figcaption>
</figure>
<figure class="kg-card kg-card-image kg-card-hascaption">
    <img src="https://www.freecodecamp.org/news/content/images/2023/06/image-184.png" alt="测试我们的 API 网关" class="kg-image" width="600" height="400" loading="lazy">
    <figcaption>测试我们的 API 网关</figcaption>
</figure>
<p>在成功测试 API 后，您就能部署 API 了。要部署 API，请再次单击 Actions 按钮，然后单击 Deploy API。</p>
<figure class="kg-card kg-card-image kg-card-hascaption">
    <img src="https://www.freecodecamp.org/news/content/images/2023/06/image-185.png" alt="部署 API 网关" class="kg-image" width="600" height="400" loading="lazy">
    <figcaption>部署 API 网关</figcaption>
</figure>
<p>此时屏幕上将弹出 Deploy API 的对话框，请选择 New Stage 作为 Deployment stage，并对其进行命名。然后，单击 Deploy 按钮。</p>
<figure class="kg-card kg-card-image kg-card-hascaption">
    <img src="https://www.freecodecamp.org/news/content/images/2023/06/image-155.png" alt="配置 API 网关部署" class="kg-image" width="600" height="400" loading="lazy">
    <figcaption>配置 API 网关部署</figcaption>
</figure>
<p>单击页面顶部的 Invoke URL，您将看到 Lambda 做出的响应。</p>
<figure class="kg-card kg-card-image kg-card-hascaption">
    <img src="https://www.freecodecamp.org/news/content/images/2023/06/image-156.png" alt="API 网关创建成功" class="kg-image" width="600" height="400" loading="lazy">
    <figcaption>API 网关创建成功</figcaption>
</figure>
<figure class="kg-card kg-card-image kg-card-hascaption">
    <img src="https://www.freecodecamp.org/news/content/images/2023/06/image-186.png" alt="测试我们的 API" class="kg-image" width="600" height="400" loading="lazy">
    <figcaption>测试我们的 API</figcaption>
</figure>
<p>真棒! 我们已经成功创建了 Rest API，将其与 Lambda 函数集成并进行了部署。</p>
<p>但是，您可以通过市场中提供的多种服务来实现这个目标，那为什么要选择 AWS API Gateway 呢？</p>
<p>嗯，这是一个有趣的问题。首先，您可以利用 AWS API Gateway 为自己的 API 配置使用计划，而其中最突出的一点就是您无需为此编写任何代码。</p>
<p>现在就让我们来创建一个使用计划，生成一个 API 密钥，并仅通过在标头中传递 API 密钥来访问 Rest API。</p>
<h2 id="apigateway">如何创建 API Gateway 使用计划</h2>
<p>在左侧边栏中单击 Usage Plans，然后单击 Create 按钮。输入您的计划名称，这里我选择了 "Basic"。根据您的需求在 Throttling 和 Quota 选项中输入相应数值，然后单击 Next。</p>
<figure class="kg-card kg-card-image kg-card-hascaption">
    <img src="https://www.freecodecamp.org/news/content/images/2023/06/image-159.png" alt="创建 AWS API Gateway 使用计划" class="kg-image" width="600" height="400" loading="lazy">
    <figcaption>创建 AWS API Gateway 使用计划</figcaption>
</figure>
<p>单击 Add API Stage 按钮，并选择相应的 API 及其 Stage。然后，单击右上角的勾选图标，并选择 Next。</p>
<figure class="kg-card kg-card-image kg-card-hascaption">
    <img src="https://www.freecodecamp.org/news/content/images/2023/06/Screenshot-from-2023-06-19-10-46-19.png" alt="为我们的 API 创建一个 Stage" class="kg-image" width="600" height="400" loading="lazy">
    <figcaption>为我们的 API 创建一个 Stage</figcaption>
</figure>
<figure class="kg-card kg-card-image kg-card-hascaption">
    <img src="https://www.freecodecamp.org/news/content/images/2023/06/image-164.png" alt="为我们的 API 创建一个 Stage" class="kg-image" width="600" height="400" loading="lazy">
    <figcaption>为我们的 API 创建一个 Stage</figcaption>
</figure>
<p>单击 "Create API Key and add to Usage Plan"，屏幕上将弹出一个对话框，请输入 API 密钥名称。而关于 API 密钥，我这里选择了 Auto Generate （自动生成），当然您也可以进行自定义。然后，单击 Save 按钮。</p>
<figure class="kg-card kg-card-image kg-card-hascaption">
    <img src="https://www.freecodecamp.org/news/content/images/2023/06/image-160.png" alt="创建 API 密钥以访问服务" class="kg-image" width="600" height="400" loading="lazy">
    <figcaption>创建 API 密钥以访问服务</figcaption>
</figure>
<figure class="kg-card kg-card-image kg-card-hascaption">
    <img src="https://www.freecodecamp.org/news/content/images/2023/06/image-161.png" alt="配置 API 密钥" class="kg-image" width="600" height="400" loading="lazy">
    <figcaption>配置 API 密钥</figcaption>
</figure>
<p>从侧边栏选择 Resources，单击已创建的 GET API，然后单击 Method Request。</p>
<figure class="kg-card kg-card-image kg-card-hascaption">
    <img src="https://www.freecodecamp.org/news/content/images/2023/06/image-162.png" alt="选择方法" class="kg-image" width="600" height="400" loading="lazy">
    <figcaption>选择方法</figcaption>
</figure>
<p>在设置选项中，将 API Key Required 字段更新为 "true" 并单击勾选图标。更新后，务必点击 Actions 下拉菜单以部署更改。否则，变更将不会更新。</p>
<figure class="kg-card kg-card-image kg-card-hascaption">
    <img src="https://www.freecodecamp.org/news/content/images/2023/06/image-187.png" alt="启用 API Key Required 字段" class="kg-image" width="600" height="400" loading="lazy">
    <figcaption>启用 API Key Required 字段</figcaption>
</figure>
<figure class="kg-card kg-card-image kg-card-hascaption">
    <img src="https://www.freecodecamp.org/news/content/images/2023/06/image-165.png" alt="部署 API" class="kg-image" width="600" height="400" loading="lazy">
    <figcaption>部署 API</figcaption>
</figure>
<p>现在点击相同的 URL，你会发现神奇的事发生了。</p>
<p>Forbidden （禁止访问）！</p>
<p>因为现在我们的 API 层已受保护，您必须在标头中传递 API 密钥才能访问数据。</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2023/06/image-163.png" alt="image-163" width="600" height="400" loading="lazy"></p>
<p>若未提供 API 密钥，则禁止访问。</p>
<p>现在单击侧边栏中的 Usage Plans，选择您的计划并导航至 API 密钥选项卡。</p>
<figure class="kg-card kg-card-image kg-card-hascaption">
    <img src="https://www.freecodecamp.org/news/content/images/2023/06/image-166.png" alt="访问您的 API 密钥" class="kg-image" width="600" height="400" loading="lazy">
    <figcaption>访问您的 API 密钥</figcaption>
</figure>
<p>单击您在步骤 3 中创建的 API 密钥，然后单击 Show， 并复制此 API 密钥。</p>
<figure class="kg-card kg-card-image kg-card-hascaption">
    <img src="https://www.freecodecamp.org/news/content/images/2023/06/image-188.png" alt="API 密钥列表" class="kg-image" width="600" height="400" loading="lazy">
    <figcaption>API 密钥列表</figcaption>
</figure>
<figure class="kg-card kg-card-image kg-card-hascaption">
    <img src="https://www.freecodecamp.org/news/content/images/2023/06/image-167.png" alt="显示您的 API 密钥" class="kg-image" width="600" height="400" loading="lazy">
    <figcaption>显示您的 API 密钥</figcaption>
</figure>
<p>您必须在 'x-api-key' 标头中传递密钥。现在，让我们切换至终端来测试一下。</p>
<p>首先，测试一下在不传递 API 密钥的情况下 Rest API 的响应。打开终端，然后输入下方的 curl 命令。此时，您将再次看到“禁止访问”的消息。</p>
<pre><code class="language-bash">curl --location --request GET '[enter your invoke url]'
--header 'Content-Type: application/json
</code></pre>
<p><img src="https://www.freecodecamp.org/news/content/images/2023/06/image-189.png" alt="image-189" width="600" height="400" loading="lazy"></p>
<p>终端中未提供 API 密钥的情况下禁止访问</p>
<p>现在再进行一次测试，在标头中传递 API 密钥，并运行下方 curl 命令：</p>
<pre><code class="language-bash">curl --location --request GET '[your invoke url]' \
--header 'x-api-key: [your api key]' \
--header 'Content-Type: application/json' \
--data-raw ''
</code></pre>
<p><img src="https://www.freecodecamp.org/news/content/images/2023/06/image-190.png" alt="image-190" width="600" height="400" loading="lazy"></p>
<p>在 x-api-key 标头中传递 API 密钥时获取的数据</p>
<p>因为在标头中传递了 'x-api-key'，所以您能看到 Lambda 函数输出的结果。</p>
<p>真棒! 您已经成功创建了使用计划，生成了 API 密钥，并将其附加到 Rest API 方法中以及验证了集成。</p>
<h2 id="">总结</h2>
<p>在本教程中，您学习了 AWS API gateway 是什么，以及如何为 Rest API 创建使用计划。</p>
<p>如果您想学习更多关于 AWS Services 的知识，可以订阅我的 <a href="https://5minslearn.gogosoon.com/?ref=fcc_aws_api_gateway">email newsletter</a> (<a href="https://5minslearn.gogosoon.com/?ref=fcc_aws_api_gateway">https://5minslearn.gogosoon.com/</a>) 并在社交媒体上关注我。</p>
<!--kg-card-end: markdown--> ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ 通过 7 个项目学习 AWS 的 Serverless ]]>
                </title>
                <description>
                    <![CDATA[ 当你开始学习 serverless 时，遵循教程是很好的第一步。但要想真正进步，你需要创建自己的项目。 问题是，想出一些既现实又能帮助你成长的想法是很难的。 为了帮助你，我想出了 7 个很棒的项目想法，以逐步帮助你成为一个更好的 serverless 开发人员。 如果你是 serverless 工作的新手，那么就从第一个项目开始。如果你以前用 Serverless 构建过 API 和 Dynamo，那么就挑选那些涵盖你最想学习的主题的项目。 如果你喜欢通过视频学习，可以查看这个视频教程 [https://www.youtube.com/watch?v=ROiAUWX8p5E&feature=emb_imp_woyt]。 组合式 API 项目 这个项目是为了让你熟悉用 Lambda 部署 API。你还将练习调用其他 API 并合并这些数据。 有很多逻辑可以用于此，但这里有两个例子：  * 将 Steam 游戏交易转换为应用你的本地货币  * 将新闻翻译成你的本地语言 这个项目的架构很简单，但这很适合作为你的第一个项目。我们的想法是，你创建的 API 将接收一些参数，所 ]]>
                </description>
                <link>https://www.freecodecamp.org/chinese/news/learn-serverless-aws-by-building-7-projects/</link>
                <guid isPermaLink="false">631746f75826d307a3f9bff2</guid>
                
                    <category>
                        <![CDATA[ AWS ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ luojiyin ]]>
                </dc:creator>
                <pubDate>Fri, 02 Sep 2022 11:18:00 +0000</pubDate>
                <media:content url="https://chinese.freecodecamp.org/news/content/images/2022/09/pexels-christina-morillo-1181316.jpeg" medium="image" />
                <content:encoded>
                    <![CDATA[ <p data-test-label="translation-intro">
        <strong>原文：</strong> <a href="https://www.freecodecamp.org/news/learn-serverless-aws-by-building-7-projects/" target="_blank" rel="noopener noreferrer" data-test-label="original-article-link">How to Learn Serverless AWS by Building 7 Projects</a>
      </p><!--kg-card-begin: markdown--><p>当你开始学习 <code>serverless</code> 时，遵循教程是很好的第一步。但要想真正进步，你需要创建自己的项目。</p>
<p>问题是，想出一些既现实又能帮助你成长的想法是很难的。</p>
<p>为了帮助你，我想出了 7 个很棒的项目想法，以逐步帮助你成为一个更好的 <code>serverless</code> 开发人员。</p>
<p>如果你是 <code>serverless</code> 工作的新手，那么就从第一个项目开始。如果你以前用 Serverless 构建过 API 和 Dynamo，那么就挑选那些涵盖你最想学习的主题的项目。</p>
<p>如果你喜欢通过视频学习，可以查看这个<a href="https://www.youtube.com/watch?v=ROiAUWX8p5E&amp;feature=emb_imp_woyt">视频教程</a>。</p>
<h1 id="api">组合式 API 项目</h1>
<p>这个项目是为了让你熟悉用 Lambda 部署 API。你还将练习调用其他 API 并合并这些数据。</p>
<p>有很多逻辑可以用于此，但这里有两个例子：</p>
<ul>
<li>将 Steam 游戏交易转换为应用你的本地货币</li>
<li>将新闻翻译成你的本地语言</li>
</ul>
<p><img src="https://completecoding.io/wp-content/uploads/2022/08/ch2.drawio.png" alt="ch2.drawio" width="600" height="400" loading="lazy"></p>
<p>这个项目的架构很简单，但这很适合作为你的第一个项目。我们的想法是，你创建的 API 将接收一些参数，所以看起来像这样：</p>
<pre><code class="language-shell">https://apiurl.amazonaws.com/dev/steamdeals?currency=EUR
https://apiurl.amazonaws.com/dev/news/fr
</code></pre>
<p>逻辑将全部写进 Lambda 中。我不打算写完整的代码，但这里有一些伪代码：</p>
<pre><code class="language-JavaScript">const handler = (event: APIGatewayProxyEvent) =&gt; {
    // get the path or query string parameter value

    // hit the first API to get the first data

    // get the data you need to translate / convert

    // pass the data from API1 into API2

    // combine the data 

    // return data in API Gateway format
} 
</code></pre>
<p>如果你需要一些免费和公共的 API 来使用，那么 <a href="https://github.com/public-apis/public-apis">https://github.com/public-apis/public-apis</a> 有一个庞大的文档供你选择。</p>
<p>然后尝试使用无服务器框架或 AWS CDK 构建它。这些工具将使你作为一个开发者的价值大大增加。</p>
<p>要开始使用 Serverless 框架，<a href="https://youtu.be/HhgXwKFUzT8">查看此视频</a>。</p>
<h1 id="">短网址器项目</h1>
<p>这个项目将让你部署你的第一个 DynamoDB 表，然后从表中写入和读取数据。</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2022/08/ch3-url-shortener.drawio.png" alt="ch3-url-shortener.drawio" width="600" height="400" loading="lazy"></p>
<p>将会有两个 API endpoints（端点）。一个是向缩短器添加新的 URL，一个是提供缩短后的 URL 并获得原始 URL。</p>
<p>下面是这两个 API 的伪代码：</p>
<pre><code class="language-javascript">// Adding a new URL
const handler = (event: APIGatewayProxyEvent) =&gt; {
    // get their original url (body of the event)
    
    // generate a random 5 character code
    
    // write to Dyanamo - {id: code, url: originalUrl }
    
    // return the url (https://{apiurl}.amazonaws.com/dev/get/{code})
}
</code></pre>
<p>为了获得 <code>apiURL</code>，我们可以把它作为一个环境变量（environment variable）传入。如果你使用的是 Serverless 框架，你可以通过在配置的环境部分添加以下内容来实现这一点：</p>
<pre><code class="language-json">apiUrl: {
  "Fn::Join": [
    "",
    [
      "https://",
      { Ref: "HttpApi" },
      ".execute-api.${self:provider.region}.amazonaws.com",
    ],
  ],
},
</code></pre>
<pre><code class="language-JavaScript">// Getting a URL by code
const handler = (event: APIGatewayProxyEvent) =&gt; {
    // get code from the url path
    
    // get from dynamo by ID
    
    // return the original url
}
</code></pre>
<p>你可以更进一步，改变 <code>getUrl</code> 代码的状态代码，返回 301（永久重定向状态码）。你需要添加一些额外的消息头（headers），但这将自动将用户重定向到所需页面。</p>
<h1 id="">提醒应用程序项目</h1>
<p>这个项目将教会你 Dynamo 的二级索引以及 Dynamo 的 Time-To-Live。你还可以尝试用亚马逊简单电子邮件服务（SES）实现电子邮件自动化，或者用简单通知服务（SNS）实现文本消息传递。</p>
<p>你还可以建立一个简单的前端应用程序，并学习在 S3 中托管它和使用 CloudFront 来分发它。</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2022/08/ch4-reminder-app.drawio.png" alt="ch4-reminder-app.drawio" width="600" height="400" loading="lazy"></p>
<p>这个应用程序的想法是，你可以向第一个 API 发送一个新的提醒。这将在 DynamoDB 中写入一条新的记录，但你将为该表添加一个全局二级索引（GSI）。这意味着你可以通过 id 获得提醒，也可以基于用户进行查询。</p>
<p>它还将有一个存活时间（TTL），这将允许你在提醒的时候触发一个 Lambda。设置提醒的代码看起来与之前的项目很相似。</p>
<p>该表将看起来像这样：</p>
<table>
<thead>
<tr>
<th>id</th>
<th>userId</th>
<th>TTL</th>
<th>notificationType</th>
<th>message</th>
</tr>
</thead>
<tbody>
<tr>
<td>123</td>
<td><a href="mailto:test@gmail.com">test@gmail.com</a></td>
<td>1648277828</td>
<td>email</td>
<td>Publish next youtube video</td>
</tr>
<tr>
<td>897</td>
<td>447113350882</td>
<td>1648842828</td>
<td>sms</td>
<td>Get More MILK</td>
</tr>
</tbody>
</table>
<p>存活时间（TTL）告诉 dynamo，一旦时间达到这个日期，就从 Dynamo 中删除该记录。</p>
<p>关于 TTL，有两件事需要注意：</p>
<ul>
<li>确保这是删除日期的 Unix 时间戳，但以秒为单位。<code>new Date('october 20 2022').getTime()</code>的单位是毫秒，所以只需除以 1000。</li>
<li>记录将在你的 TTL 之后的 15 分钟内被删除，所以如果已经过了 5 分钟，记录还没有被删除，也不要惊慌。</li>
</ul>
<p>然后，你可以设置第二个 Lambda，当记录从 Dynamo 被删除时，它会被触发。然后将信息发送到他们的电子邮件或电话。</p>
<pre><code class="language-javascript">// Send reminder
const handler = async (event: DynamoDBStreamEvent) =&gt; {
    // get the list of deleted records
    
    // map over each record
       // Call SES or SNS to send the reminder
}
</code></pre>
<p>第二个 API 将是获取一个用户的所有提醒信息。确保你已经设置了一个 GSI （全局二级索引），分区键（partition key）为<code>userId</code>，排序键（sort key）为<code>TTL</code>。</p>
<pre><code class="language-javascript">// Get My Reminders
const handler = (event: APIGatewayProxyEvent) =&gt; {
    // get the userId from request
    
    // params = KeyConditionExpression: "'userId' = userId",
    
    // Query Dynamo
    
    // format the reminders 
    
    // return to the user
}
</code></pre>
<p>对于前端，你可以写一个简单的网络应用，包括两个部分：创建新的提醒，以及列出我的提醒。这可以是 React、Vue、Angular，甚至是原生 HTML、CSS 和 JavaScript。</p>
<p>一旦你有了这个应用，你可以使用 <code>serverless-s3-sync</code> 来自动推送代码到 S3（你在 Serverless 中创建的）。</p>
<h1 id="">即时聊天应用程序项目</h1>
<p>这个项目将教你如何建立 WebSockets。用户可以创建一个新的房间或加入一个现有的房间。任何发送的信息都会被发送到连接到房间的所有人那里。</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2022/08/ch5-live-chat.drawio.png" alt="ch5-live-chat.drawio" width="600" height="400" loading="lazy"></p>
<p>WebSockets 的工作方式与常规 API 略有不同。你不是有多个端点（endpoints），而是有一个端点和不同的消息类型。</p>
<p>有一些默认的消息类型和一些自定义的消息类型：</p>
<ul>
<li>connectToRoom – Custom</li>
<li>createRoom – Custom</li>
<li>onMessage – Custom</li>
<li>disconnect – Default</li>
</ul>
<p>当用户连接到 WebSocket 时，他们可以发送一个 <code>connectToRoom</code> 或 <code>createRoom</code> 消息。这两种方式都将在 Dynamo 中创建一条记录，其中包含用户的 WebSocket <code>connectionId</code>和 <code>roomId</code>。正如我们之前所做的，我们将在 Dynamo 中建立一个 GSI，这样我们以后就可以通过查询来获得所有用户的<code>roomId</code>。</p>
<p><code>connectToRoom</code>和<code>createRoom</code>的代码将与以前的 <code>向Dynamo写数据</code> 的 lambdas 非常相似。</p>
<p>你可能想在 <code>connectToRoom</code> 中首先检查房间是否存在。你可以通过查询所有用户的 roomId 来做到这一点。如果房间里没有用户，那就意味着他们正试图连接到一个已经不存在的房间。</p>
<p>现在一个用户在一个房间里，他们可以发送一个消息。以下是该 Lambda 的伪代码：</p>
<pre><code class="language-JavaScript">// onMessage
const handler = (event: WebsocketMessageEvent) =&gt; {
    // get the user's connectionId
    
    // get the user by connectionId from Dynamo to get the roomId
    
    // query for all users in that roomId
    
    // send the message to all users
    
    // return
}
</code></pre>
<p>最后是 onDisconnect，这将是一个简单的 Lambda，只是将用户的记录从 Dynamo 中删除。</p>
<h1 id="">创意投票应用项目</h1>
<p>这个项目将教你设计和建立更高级的 Dynamo 表，同时与 Cognito 合作进行认证。你还应该为此建立一个简单的前端，以学习如何将 Cognito 集成到 Web 应用中。</p>
<p>这个工具允许你向你的社区征求创意，并找出其中最受欢迎的。</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2022/08/ch6-idea-voting-app.drawio.png" alt="ch6-idea-voting-app.drawio" width="600" height="400" loading="lazy"></p>
<p>这要从所有用户需要在你的应用程序开始时注册。我们使用 Cognito，这样做是为了确保每个人只能投票一次。</p>
<p>然后你需要创建几个 API 端点：</p>
<ul>
<li>create a board</li>
<li>add an idea to a board</li>
<li>vote for an idea</li>
<li>get board details</li>
</ul>
<h3 id="">创建一个板块</h3>
<p>我们需要的第一个 API 端点是用来创建一个新的创意板。任何人都可以创建一个论坛，Dynamo 中的记录非常简单。只有一个 "boardId"，然后是一些关于板块所有者的信息，也许是一个标题和描述。</p>
<table>
<thead>
<tr>
<th>boardId</th>
<th>owner</th>
<th>title</th>
<th>description</th>
</tr>
</thead>
<tbody>
<tr>
<td>boardId</td>
<td>userId</td>
<td>My awesome idea</td>
<td>some description</td>
</tr>
</tbody>
</table>
<h3 id="">将想法添加到一个板块中</h3>
<p>接下来，我们需要能够将想法添加到一个板块（board）。这将是另一个 API 端点和 Lambda。对于想法数据库的记录，我们需要更聪明一些，因为我们需要能够直接引用一个想法，但也要获得一个板块（board）的所有想法。</p>
<p>为了做到这一点，我们有一个这样的模式。<code>pk</code>是一个分区键，<code>sk</code>是一个排序键，在 DynamoDB 上查询时都需要这两个键。</p>
<table>
<thead>
<tr>
<th>id</th>
<th>pk</th>
<th>sk</th>
<th>idea</th>
<th>owner</th>
</tr>
</thead>
<tbody>
<tr>
<td>id</td>
<td>boardId</td>
<td>ideaId</td>
<td>the idea</td>
<td>userId</td>
</tr>
</tbody>
</table>
<p>通过 "id"，我们可以直接引用这个想法，但我们也可以查询这个记录。我们可以通过查询<code>pk = 1234</code> 来获得董事会<code>1234</code> 的所有想法。</p>
<h3 id="">对该想法进行投票</h3>
<p>现在我们有了想法，我们需要我们的用户为他们投票。这将是一个新的 API 端点和 Lambda。这个 Lambda 比其他两个有更多的工作要做。首先，我们要看一下这个记录的模式。</p>
<table>
<thead>
<tr>
<th>pk</th>
<th>sk</th>
<th>pk2</th>
<th>sk2</th>
</tr>
</thead>
<tbody>
<tr>
<td>ideaId</td>
<td>userId</td>
<td>userId</td>
<td>ideaId</td>
</tr>
</tbody>
</table>
<p>这初看起来很奇怪，但我会解释为什么我们要这样构建它。</p>
<p>对于一个给定的想法，我们想知道它有多少票。我们可以通过查询 <code>pk = ideaId</code> 来了解，这将返回该想法的所有投票。</p>
<p>当添加投票时，我们要检查用户是否已经为该想法投票。我们可以通过查询<code>pk = ideaId &amp;&amp; sk = userId</code> 来做到这一点。如果我们找到一个匹配的记录，我们就知道他们已经为这个想法投了票。如果没有，我们就可以为这个用户和想法添加<code>投票</code>记录。</p>
<h3 id="">获取详情</h3>
<p>我们现在可以写一个 lambda，来查询我们所拥有的数据：</p>
<pre><code class="language-JavaScript">export const handler = async (event: APIGatewayProxyEvent) =&gt; {
    // get boardId from the request
    // query all ideas on the board
    // map over each idea
        // query for all votes on this idea
    // format it all into a nice format
    // return to the user
}
</code></pre>
<h3 id="">为用户获取投票</h3>
<p>最后，我们希望能够显示一个给定用户的所有投票。我们不能查询<code>sk = userId</code>，因为在查询 DynamoDB 时你总是需要一个分区键。因此，我们创建了第二个分区键（<code>pk2</code>），其中包含了 userId。现在我们可以查询<code>pk2 = userId</code>来获得用户投票的所有想法。</p>
<p>这种拥有第二个 GSI 的模式非常普遍，其中 PK 和 SK 是对调的。它允许你有一个多对多的关系。你可以查询所有连接到特定 A 的 B 和所有连接到特定 B 的 A。</p>
<h2 id="">信息化应用项目</h2>
<p>这个项目将教你了解 Dynamo 的复合键，并给你更多关于 websockets 和 Cognito 的练习。</p>
<p>这个应用程序允许用户注册，请求加入一个房间，然后查看该房间内已发送的所有信息。一个房间的主人决定是否让某人加入该房间。</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2022/08/ch7-messaging-app-api.drawio.png" alt="ch7-messaging-app-api.drawio" width="600" height="400" loading="lazy"></p>
<p>架构开始时看起来很像即时聊天应用程序--有一个 WebSocket 连接，有<code>onConnect, onDisconect, create</code> Group, <code>joinGroup, listMyGroups handleJoinGroupRequest</code>和<code>sendMessage</code>的 Lambda。</p>
<p>在我们开始 Lambda 代码之前，我们要把 Cognito 添加到应用程序中，这样我们就可以让用户注册了。这将在以后用户请求访问某个组时使用。</p>
<p><code>createGroup</code> Lambda 与即时聊天解决方案几乎相同。它只是创建一个简单的 <code>群组（group）</code>记录。</p>
<h3 id="websocketconnectdisconnect">Websocket Connect / Disconnect</h3>
<p>现在，当一个用户登录并连接到 WebSocket 时，我们可以创建一个 onConnection Lambda。这将获得用户身份并验证他们的 Cognito 用户令牌。这个令牌将很容易给我们提供 userId 和 userName。</p>
<p>如果他们没有传递令牌（token），或者令牌无效（或者已经过期），那么我们可以杀死 websocket，阻止他们在没有有效令牌的情况下做任何事情。</p>
<p>如果令牌是有效的，那么我们可以存储一个简单的连接记录。这将使我们能够在以后向 websocket 发送消息。</p>
<p>只需记住在用户断开与 WebSocket 的连接时删除该记录。我们可以使用默认的 <code>$disconnect</code> 动作来触发<code>onDisconnect</code> lambda。</p>
<h3 id="">创建一个组</h3>
<p>有人需要做的第一件事是创建一个小组，然后邀请他们的朋友加入。</p>
<p>这将涉及在 Dynamo 中创建两条记录。第一条是小组的记录。这将包括群组 ID、群组名称，以及谁是群组所有者。</p>
<p>第二条将是一个组的成员记录。这将是说这个用户是这个组的一部分。</p>
<table>
<thead>
<tr>
<th>pk</th>
<th>sk</th>
<th>pk2</th>
<th>sk2</th>
<th>userName</th>
<th>groupName</th>
</tr>
</thead>
<tbody>
<tr>
<td>groupId</td>
<td>user#{userId}</td>
<td>userId</td>
<td>groupId</td>
<td>user name</td>
<td>group name</td>
</tr>
</tbody>
</table>
<p>这第一部分将允许我们查询 <code>pk = groupId and sk startsWith('user')</code> 以获得该组的所有用户。这将用于 <code>sendMessage</code> Lambda，以获得所有的用户来发送 WebSocket 消息。</p>
<p>添加第二部分（PK2，SK2）是为了让我们通过查询 <code>PK2 = userId</code> 来获得用户授权的所有组。这在我们需要获得所有用户组的列表时使用。</p>
<h3 id="">加入一个组</h3>
<p>现在，<code>加入(join)</code> 组请求将在 Dynamo 中创建一个 <code>访问请求(access request)</code> 的记录。这不会让用户访问该组，但将允许该组的所有者审查访问请求。</p>
<p>这些记录将是我们第一次使用复合键。这是将排序键提升到一个新的层次，记录将看起来像这样：</p>
<table>
<thead>
<tr>
<th>pk</th>
<th>sk</th>
</tr>
</thead>
<tbody>
<tr>
<td>groupId</td>
<td>joinRequest#{userId}</td>
</tr>
</tbody>
</table>
<p>这使我们能够查询 <code>pk = groupId and sk startsWith('joinRequest')</code> 的地方。这将返回该组的所有访问请求。当有人提出这个请求时，我们可以首先检查他们是否是该组的所有者。</p>
<p>然后组主有两个选择——接受（accept）或拒绝（reject）。如果所有者拒绝该用户，我们可以直接删除访问请求记录。如果他们接受该用户，我们需要添加一个 <code>授权用户（authorised user）</code>。这将需要向 Dynamo 添加一条新的记录。</p>
<h3 id="">发送信息</h3>
<p>现在我们进入了消息应用的核心，发送和存储消息。</p>
<p>在 <code>sendMessage</code> WebSocket Lambda 中，我们可以查询组内的所有用户，并向所有当前连接发送消息。我们还需要在 Dynamo 中存储消息。</p>
<table>
<thead>
<tr>
<th>pk</th>
<th>sk</th>
<th>message</th>
<th>user</th>
</tr>
</thead>
<tbody>
<tr>
<td>groupId</td>
<td>message#{timestamp}</td>
<td>my message</td>
<td>userId</td>
</tr>
</tbody>
</table>
<h3 id="">获取以前的信息</h3>
<p>当用户登录时，他们需要能够得到他们在离线时错过的信息。有了这样的信息数据结构，我们可以查询到：</p>
<p><code>pk = groupId and sk &gt; 'message#{ timestamp for yesterday }'</code></p>
<p>这将返回从昨天开始创建的所有消息。</p>
<p>我们也可以让他们通过传递他们的最后一条消息来获得更早的消息。这将使我们能够得到下一条信息。这将使我们能够在信息历史上创建一个无限的向上滚动。</p>
<h2 id="">事件驱动的电子商务系统项目</h2>
<p>这个项目将教会你有关事件桥的知识，并给你一些有关 DynamoDB 表设计和服务的额外练习，如 SES 和 SNS 的电子邮件和文本。</p>
<p>这个系统将有产品和过滤，购物车和订单，正如你所期望的。这里的关键是，订单的下达、订单状态的改变和交付的更新都将通过 Event Bridge 来处理。</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2022/08/ch8-event-ecomerce.drawio.png" alt="ch8-event-ecomerce.drawio" width="600" height="400" loading="lazy"></p>
<h3 id="">储存产品</h3>
<p>首先，我们需要存储产品。我们可以用一种方式来构造我们的排序键，以便让我们的产品有一个层次的划分。</p>
<table>
<thead>
<tr>
<th>id</th>
<th>pk</th>
<th>sk</th>
<th>title</th>
<th>description</th>
<th>...</th>
</tr>
</thead>
<tbody>
<tr>
<td>productId</td>
<td>clothing</td>
<td>mens#tops#{productId}</td>
<td>Next Slimfit T-Shirt</td>
<td>...</td>
<td>...</td>
</tr>
<tr>
<td>productId</td>
<td>clothing</td>
<td>womens#trousers#{productId}</td>
<td>Levi's Jeans</td>
<td>...</td>
<td>...</td>
</tr>
</tbody>
</table>
<p>这使得我们可以查询<code>pk = clothing and sk beginsWith mens</code>以获得所有男士的衣服，或者查询<code>pk = clothing and sk beginsWith womens#trousers</code>以获得所有女士的裤子。</p>
<h3 id="">下订单</h3>
<p>接下来的部分是能够下订单。我们不打算尝试付款，只是向 dynamoDB 表添加一个订单记录。</p>
<h3 id="eventbridge">Event Bridge</h3>
<p>这里最大的区别是，我们将开始使用 Event Bridge。这是一个工具，它允许我们拥有可以触发多个 lambdas 的事件。这很好，因为我们可以添加一个新的监听器，而不需要改变前期的代码。</p>
<h3 id="">创建订单</h3>
<p>我们将有两个监听 “orderCreated” 事件的 lambdas。第一个将获取订单数据，并将其发送到仓库 API 以获得包装。第二个将向用户发送一封订单确认邮件。</p>
<h3 id="">订单包装</h3>
<p>我们要假装有一个真正的仓库，在得到订单的装箱单后，他们打包，让订单准备发货。他们有一个系统调用我们的 API，并将订单状态改为`packed'。</p>
<p>Dynamo 记录中的这一变化将触发另一个`orderPacked'的 Event Bridge 事件。这也有两个监听器：一个是通过电子邮件向用户发送更新信息，另一个是通过电子邮件让快递公司从仓库收取包裹并交付给客户。</p>
<h3 id="ordershipped">已发货的订单（Order Shipped）</h3>
<p>同样的，我们要假装一个快递公司拿了包裹并交付。他们调用另一个<code>Order Shipped</code>的 API 端点，这又改变了数据库中的订单状态。</p>
<p>这触发了另一个 “orderDelivered” 的事件，这个事件有两个监听器：</p>
<p>一个用于向客户发送“感谢”信息。</p>
<p>另一个将做一些不同的事情。它将接收订单，删除任何个人数据，并将其存储到另一个 DynamoDB 表。</p>
<p>这是作为数据科学家的准备步骤而变得越来越普遍的事情。我们删除个人数据，以减少法律问题，但仍然允许数据科学家做一些事情，如训练一个模型，给你 <code>人们也买了。。。</code> 之类的建议。</p>
<h2 id="">下一步是什么</h2>
<p>如果你喜欢这些项目的想法，但不知道从哪里开始，那么我有一个完整的视频课程，将教你如何建立所有这些项目。</p>
<p><a href="https://completecoding.mykajabi.com/7-serverless-projects">在此查看该课程</a>。</p>
<p>你还可以下载这些项目的<a href="https://completecoding.mykajabi.com/7-practical-project-pdf">免费 PDF</a>，这样你就可以在学习过程中直观地看到你的进展。</p>
<!--kg-card-end: markdown--> ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ 如何使用 AWS SES、Lambda 和 API Gateway 从网站的“联系我们”表单接收电子邮件 ]]>
                </title>
                <description>
                    <![CDATA[ 原文：How to Receive Emails from Your Site's "Contact Us" form Using AWS SES, Lambda, & API Gateway [https://www.freecodecamp.org/news/how-to-receive-emails-via-your-sites-contact-us-form-with-aws-ses-lambda-api-gateway/] ，作者：Adham El Banhawy [https://www.freecodecamp.org/news/author/adham-el-banhawy/] 我最近在为一个客户建立一个简单的登陆页面网站，该客户希望通过他们的网站接收电子邮件，而不需要分享他们的电子邮件。 说实话，我以前从未尝试过自己实现这一功能。我总是习惯于有一个简单的 “Contact Us” 按钮，有一个锚标签（anchor），在 href  属性里有一个  mailto，像这样： <button>  <a href="mailto:myemail@example.com" ]]>
                </description>
                <link>https://www.freecodecamp.org/chinese/news/how-to-receive-emails-via-your-sites-contact-us-form-with-aws-ses-lambda-api-gateway/</link>
                <guid isPermaLink="false">627d16b9c9c067061df8b92f</guid>
                
                    <category>
                        <![CDATA[ AWS ]]>
                    </category>
                
                    <category>
                        <![CDATA[ API ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Lambda ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ luojiyin ]]>
                </dc:creator>
                <pubDate>Thu, 12 May 2022 10:16:00 +0000</pubDate>
                <media:content url="https://chinese.freecodecamp.org/news/content/images/2022/05/605b0cfb687d62084bf6bd50.jpeg" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>原文：<a href="https://www.freecodecamp.org/news/how-to-receive-emails-via-your-sites-contact-us-form-with-aws-ses-lambda-api-gateway/">How to Receive Emails from Your Site's "Contact Us" form Using AWS SES, Lambda, &amp; API Gateway</a>，作者：<a href="https://www.freecodecamp.org/news/author/adham-el-banhawy/">Adham El Banhawy</a></p><!--kg-card-begin: markdown--><p>我最近在为一个客户建立一个简单的登陆页面网站，该客户希望通过他们的网站接收电子邮件，而不需要分享他们的电子邮件。</p>
<p>说实话，我以前从未尝试过自己实现这一功能。我总是习惯于有一个简单的 “Contact Us” 按钮，有一个锚标签（anchor），在 <em>href</em> 属性里有一个 <code>mailto</code>，像这样：</p>
<pre><code class="language-html">&lt;button&gt;
 &lt;a href="mailto:myemail@example.com"&gt;Contact Me&lt;/a&gt;
&lt;/button&gt;

</code></pre>
<p>但这种方法有两个不便之处：</p>
<ol>
<li>它迫使双方，即想发送消息的用户和接收消息的网站所有者，彼此分享他们的电子邮件。虽然这对某些人来说是可以的，但对注重隐私的人来说，这并不理想。</li>
<li>对于网站访问者来说，点击链接迫使他们打开他们设备上的默认邮件程序，这可能是令人沮丧的。如果他们使用的是一台公共电脑呢？如果他们没有登录呢？如果他们根本不想使用他们的邮件程序呢？<br>
是的，从技术上讲，他们可以直接抓取收件人的电子邮件地址，并通过他们的浏览器或他们登录的地方发送消息。但这些都是额外的步骤和障碍，会使用户不愿意发送他们的信息，企业可能会失去潜在的反馈或机会。</li>
</ol>
<p>出于这个原因，我们选择了一个电子邮件表格，用户可以简单地写下他们的信息并点击提交，在不离开网站的情况下向网站的主人发送电子邮件。</p>
<p>谷歌快速搜索显示，有一些第三方工具/小工具可以嵌入到网站中，但它们需要付费订阅才能完全定制（译者注：免费版的，有功能上或者数量上的限制，或者有广告插入）。</p>
<p>除非你使用的是像 WordPress 这样的 CMS，有一个内置的/插件可以做到这一点，否则这就是一个不方便的需要经常消费的项目。</p>
<p>我选择了自己编写该功能的代码，这样我就可以完全控制了。</p>
<p>为了本指南的目的，我将重现我使用 HTML 和 AWS 服务实现该功能的步骤。</p>
<h2 id="html">HTML 表格</h2>
<p>我将在这里保持超级简单，使用一个没有 CSS 的基本 HTML 表单，只是为了测试我们想要的功能。</p>
<pre><code class="language-html">&lt;h2&gt;Contact Us&lt;/h2&gt;
&lt;form&gt;
  &lt;label for="name"&gt;Name:&lt;/label&gt;
  &lt;input name="name" type="text"/&gt;&lt;br/&gt;&lt;br/&gt;
  &lt;label for="email"&gt;Email:&lt;/label&gt;
  &lt;input name="email" type="email"/&gt;&lt;br/&gt;&lt;br/&gt;
  &lt;label for="name"&gt;Message:&lt;/label&gt;
  &lt;textarea name="message"&gt;&lt;/textarea&gt;&lt;br/&gt;&lt;br/&gt;
  &lt;input type="submit"/&gt;
  &lt;div&gt;
    &lt;p id="result-text"&gt;&lt;/p&gt;
  &lt;/div&gt;
&lt;/form&gt;

</code></pre>
<p><img src="https://www.freecodecamp.org/news/content/images/2021/03/image-61.png" alt="image-61" width="600" height="400" loading="lazy"></p>
<p>这里没有什么花哨的东西可看......</p>
<p>现在我们要用 JavaScript 来处理提交功能。</p>
<pre><code class="language-js">const form = document.querySelector('form')
form.addEventListener('submit', event =&gt; {
  // prevent the form submit from refreshing the page
  event.preventDefault()

  const { name, email, message } = event.target
  console.log('Name: ', name.value)
  console.log('email: ', email.value)
  console.log('Message: ', message.value)

})

</code></pre>
<p>在这一点上，我们有一个从用户那里获得输入的表单，以及只是将结果显示在控制台(console) 的 JavaScript 代码。</p>
<p>我们现在可以先不考虑这个问题，而是开始处理后端服务，这些后端服务将接收表单数据，并将这些数据发送电子邮件。</p>
<h2 id="">后端简介</h2>
<p>让我们深入了解 AWS，以及我们将使用哪些服务和如何使用。</p>
<p>正如标题中提到的，我们将使用 <strong>AWS Lambda</strong> 和 <strong>Simple Email Service</strong>（SES）。SES 是一个无服务器 (serverless) 的消息传递服务，允许你在调用时发送电子邮件。AWS Lambda 允许你编写服务器/端代码，以响应事件的发生而执行。</p>
<p>我们还将使用 <strong>API 网关</strong>，使我们能够通过 HTTP 调用 Lambda 函数。</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2021/03/image-62.png" alt="image-62" width="600" height="400" loading="lazy"></p>
<p>在这种情况下，当我们的表单被提交时，将发生以下工作流程：</p>
<ol>
<li>我们的浏览器（JavaScript）将向 AWS API Gateway 指定的端点(endpoint) URL 发出一个 post 请求，请求体中包含表单数据</li>
<li>API 网关将验证这个请求。然后它将触发 Lambda 函数，该函数接受一个事件参数。API Gateway 将把表单数据放在事件参数的 body 属性中。</li>
<li>我们的 Lambda 函数将从事件主体中提取数据，然后我们将使用这些数据来建立我们想要发送的电子邮件的主体以及它的收件人。然后，我们的函数将使用 AWS SDK 来调用 SES 的电子邮件数据。</li>
<li>当 SES 收到 <em>sendMail</em> 请求，它就会将电子邮件数据变成实际的文本电子邮件，并通过 AWS 自己的邮件服务器将其发送给收件人。<br>
一旦电子邮件被发送，我们的浏览器将收到一个状态代码为 200 的响应和一个成功信息。如果 AWS 云中的任何步骤失败，响应将有一个 500 状态代码。</li>
</ol>
<h2 id="1ses">第 1 步：如何建立 SES</h2>
<p>实际上，我们将按照相反的顺序来设置每一个步骤，从 SES 开始，这将会更容易。</p>
<p>首先在你的 AWS 控制台，进入 <code>SES service</code> —&gt; 然后点击侧面菜单中的 <code>Email Addresses</code> —&gt; 然后点击 "Verify a New Email Address" 按钮。</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2021/03/image-63.png" alt="image-63" width="600" height="400" loading="lazy"></p>
<p>在打开的对话框中，输入你希望 SES 服务在发送电子邮件时的发件人地址。</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2021/03/image-64.png" alt="image-64" width="600" height="400" loading="lazy"></p>
<p>这将向你输入的电子邮件地址发送一封电子邮件，其中有一个链接，可以点击验证。AWS 将知道电子邮件的所有者同意将他们的电子邮件地址作为发件人地址。</p>
<p>在你验证电子邮件之前，SES 电子邮件仪表板将保持验证状态为待定（pendin verification）。</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2021/03/image-65.png" alt="image-65" width="600" height="400" loading="lazy"></p>
<p>当电子邮件所有者打开他们从 AWS 收到的电子邮件，并点击其中的验证链接，验证状态应该变为已验证（verified ，刷新页面以看到变化）。</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2021/03/image-66.png" alt="image-66" width="600" height="400" loading="lazy"></p>
<p>而这就是你为 SES 所要做的一切。你可以选择测试服务，在列表中选择你经过验证的电子邮件，然后点击 “Send a Test Email” 按钮。这将让你输入收件人的电子邮件地址、主题（subject）和信息（message）并发送。</p>
<p>发送的电子邮件将由 AWS 服务器签署，发件人应该是你输入的发件人地址。它应该看起来像这样：</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2021/03/image-67.png" alt="image-67" width="600" height="400" loading="lazy"></p>
<h2 id="2lambda">第 2 步：如何设置 Lambda</h2>
<p>现在这是最有趣的部分。我们将创建一个函数，用来接收表单数据并调用 SES。</p>
<p>Lambda 函数的好处是，你不必担心在服务器上 24/7 运行你的后端代码，也不必担心维护服务器。它是 <em>无服务器的（serverless）</em>。</p>
<p>但这并不意味着不涉及服务器。AWS 将幕后处理这些问题，因此你可以只专注于编写代码，而不是维护服务器。此外，你只需按你的函数被调用的次数和执行的时间来收费，而且是 <a href="https://aws.amazon.com/lambda/pricing/">难以置信的便宜</a>！</p>
<h3 id="iam">创建一个 IAM 角色并进行配置</h3>
<p>在我们开始编写 lambda 函数之前，我们需要创建一个 IAM <em>role（角色）</em>，将其附加到函数上，并授予它调用 SES 服务的权限（在 AWS 中被称为策略）。</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2021/03/image-68.png" alt="image-68" width="600" height="400" loading="lazy"></p>
<p>从你的 AWS 控制台，进入 IAM service-&gt;点击侧面菜单中的 <code>service</code> -&gt;然后点击 <code>Create Policy</code> 按钮。</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2021/03/image-69.png" alt="image-69" width="600" height="400" loading="lazy"></p>
<p>在策略创建页面，进入 JSON 标签，粘贴以下权限，然后点击 <code>Next</code>。</p>
<pre><code class="language-json">{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Sid": "VisualEditor0",
            "Effect": "Allow",
            "Action": [
                "ses:SendEmail",
                "ses:SendRawEmail"
            ],
            "Resource": "*"
        }
    ]
}

</code></pre>
<p>在第三个屏幕中，为政策命名，并点击 <code>Create Policy</code> 按钮。</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2021/03/image-70.png" alt="image-70" width="600" height="400" loading="lazy"></p>
<p>现在我们创建一个 IAM <em>role(角色)</em>，它将被附加到 lambda 上，并将其与我们刚刚创建的权限策略联系起来。</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2021/03/image-71.png" alt="image-71" width="600" height="400" loading="lazy"></p>
<p>从 IAM 侧面的菜单，点击角色，然后点击 <code>Create role</code> 按钮。</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2021/03/image-72.png" alt="image-72" width="600" height="400" loading="lazy"></p>
<p>在角色创建界面，确保选择的类型是 “AWS service”，并选择 <code>Lambda case</code>，然后点击 “Next:Permissions” 按钮。</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2021/03/image-73.png" alt="image-73" width="600" height="400" loading="lazy"></p>
<p>在下一个屏幕上，按名称搜索我们先前创建的 <code>policy</code> 并选择它，然后点击 <code>Next</code>。</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2021/03/image-74.png" alt="image-74" width="600" height="400" loading="lazy"></p>
<p>查看屏幕，给这个角色起一个你能记住的名字，然后点击 “Create role”。</p>
<p>现在我们可以创建一个新的 lambda 函数。转到 Lambda 服务仪表板，点击 “Create Function” 按钮。</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2021/03/image-75.png" alt="image-75" width="600" height="400" loading="lazy"></p>
<p>在函数创建界面，命名你的函数，选择 “Author from scratch” 选项，并选择 Node.js 作为运行时间。</p>
<p>在 “Change default execution role（改变默认执行角色）”下，选择 “Use an existing role（使用现有角色）”选项，然后从 “Existing role（现有角色）”下拉菜单中选择你在前一步创建的角色名称。</p>
<p>最后，点击 “Create function” 按钮来创建函数。</p>
<h3 id="">编写代码并测试它</h3>
<p><img src="https://www.freecodecamp.org/news/content/images/2021/03/image-76.png" alt="image-76" width="600" height="400" loading="lazy"></p>
<p>在编辑器中，打开 index.js 文件（这是在你的 lambda 被调用时将被执行的文件），用以下代码替换它：</p>
<pre><code class="language-js">const aws = require("aws-sdk");
const ses = new aws.SES({ region: "us-east-1" });
exports.handler = async function (event) {
  console.log('EVENT: ', event)
  const params = {
    Destination: {
      ToAddresses: ["your@email.com"],
    },
    Message: {
      Body: {
        Text: {
            Data: `Hello from Lambda!`
        },
      },
      Subject: { Data: `Message from AWS Lambda` },
    },
    Source: "your@email.com",
  };

  return ses.sendEmail(params).promise()
};

</code></pre>
<p>请注意，在第 2 行，我们正在使用 AWS SDK 并创建一个 SES 实例。我之所以选择 <strong>us-east-1</strong> 作为区域，是因为那是我注册和验证电子邮件的地方。请确保你的电子邮件使用你注册电子邮件的 AWS 地区。</p>
<p>现在要测试这个功能，点击 “Deploy” 按钮。然后点击 “Test” 按钮-&gt;配置测试事件，这将打开一个测试配置对话框，你可以创建一个新的测试事件。</p>
<p>在测试事件主体编辑器中，输入以下 JSON，模仿最终将来自我们的浏览器请求的内容。然后点击创建。</p>
<pre><code class="language-json">{
  "body": {
        "senderName": "Namo",
        "senderEmail": "namo@trains.com",
        "message": "I love trains!"
    }
}

</code></pre>
<p>现在点击 <code>test</code> 按钮将运行我们刚刚创建的测试。它应该在编辑器中打开一个新的标签，向我们展示运行该函数所产生的日志，它应该是这样的：</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2021/03/image-77.png" alt="image-77" width="600" height="400" loading="lazy"></p>
<p>注意，我们登录的事件对象在这里显示在功能日志下，其中有我们在测试事件中使用的主体数据。</p>
<p>This test should have sent an email to my inbox as well – let's see if that happened.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2021/03/image-78.png" alt="image-78" width="600" height="400" loading="lazy"></p>
<p>是的，就像预期的那样。而这几乎是在运行测试后立即发生的。</p>
<p>现在让我们修改我们的函数代码，从测试数据中得到一个更有意义的信息。</p>
<pre><code class="language-js">const aws = require("aws-sdk");
const ses = new aws.SES({ region: "us-east-1" });
exports.handler = async function (event) {
  console.log('EVENT: ', event)
 // Extract the properties from the event body
  const { senderEmail, senderName, message } = JSON.parse(event.body)
  const params = {
    Destination: {
      ToAddresses: ["the.benhawy@gmail.com"],
    },
  // Interpolate the data in the strings to send
    Message: {
      Body: {
        Text: {
            Data: `You just got a message from ${senderName} - ${senderEmail}:
            ${message}`
        },
      },
      Subject: { Data: `Message from ${senderName}` },
    },
    Source: "the.benhawy@gmail.com",
  };

  return ses.sendEmail(params).promise();
};

</code></pre>
<p>需要注意的是，当 API Gateway 调用我们的函数时，它将传递一个字符串给事件主体（event body）。这就是为什么我在 event.body 上使用<code>JSON.parse</code>，把它变成 JSON 并提取我们发件人的电子邮件、姓名和信息。然后我在邮件正文文本和主题中使用这些变量，使用字符串插值。</p>
<p>如果你尝试测试它，代码将返回一个错误。这是因为测试将一个 JSON 对象传递给 event.body，而我们在 JSON 上使用 JSON.parse，这在 JavaScript 中会导致错误。</p>
<p>遗憾的是，测试编辑器不允许我们向事件传递字符串，所以我们必须在以后从别的地方测试。</p>
<h2 id="3api">第 3 步：如何设置 API 网关</h2>
<p>接下来，我们要使用的最后一项 AWS 服务是 API 网关，它将使我们的浏览器能够向我们创建的 Lambda 函数发送 HTTP 请求。</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2021/03/image-79.png" alt="image-79" width="600" height="400" loading="lazy"></p>
<p>不用离开你的 lambda 函数页面，展开 “Function overview” 部分并点击 “Add trigger（添加触发器）”。</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2021/03/image-80.png" alt="image-80" width="600" height="400" loading="lazy"></p>
<p>接下来，从下拉菜单中选择 <code>API Gateway</code> ，<code>HTTP API</code> 作为 API 类型，"Open(开放)" 作为安全策略，并选中 <code>CORS</code> 复选框选项。然后点击 "Add"。</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2021/03/image-81.png" alt="image-81" width="600" height="400" loading="lazy"></p>
<p>你应该被重定向到你的函数的 “Configuration” 标签，显示你刚刚创建的新的 API 网关触发器。在那里，注意 <strong>API endpoint</strong>。这就是接收从浏览器中表单发出数据的 URL。</p>
<h2 id="html">回到 HTML</h2>
<p>我们最后可以测试一下这个表单，看看它是否发送了邮件。</p>
<p>让我们修改我们的 JavaScript 来处理表单提交时的发送请求。</p>
<pre><code class="language-js">const form = document.querySelector("form");
form.addEventListener("submit", (event) =&gt; {
  // prevent the form submit from refreshing the page
  event.preventDefault();

  const { name, email, message } = event.target;

 // Use your API endpoint URL you copied from the previous step
  const endpoint =
    "&lt;https://5ntvcwwmec.execute-api.us-east-1.amazonaws.com/default/sendContactEmail&gt;";
  // We use JSON.stringify here so the data can be sent as a string via HTTP
 const body = JSON.stringify({
    senderName: name.value,
    senderEmail: email.value,
    message: message.value
  });
  const requestOptions = {
    method: "POST",
    body
  };

  fetch(endpoint, requestOptions)
    .then((response) =&gt; {
      if (!response.ok) throw new Error("Error in fetch");
      return response.json();
    })
    .then((response) =&gt; {
      document.getElementById("result-text").innerText =
        "Email sent successfully!";
    })
    .catch((error) =&gt; {
      document.getElementById("result-text").innerText =
        "An unkown error occured.";
    });
});

</code></pre>
<p>现在，是关键时刻：填写表格并点击提交。如果你看到成功信息，这意味着电子邮件已经发送。</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2021/03/image-82.png" alt="image-82" width="600" height="400" loading="lazy"></p>
<p>由于我向自己的邮箱发送电子邮件，因此我快速查看我的收件箱，以查看我收到了一封来自我自己的电子邮件，其中包含我在表单中使用的详细信息！</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2021/03/image-83.png" alt="image-83" width="600" height="400" loading="lazy"></p>
<p>跟着教程，你现在拥有一个功能正常的 “Contact Us” 的表单，你可以将其插入任何网站。 而且你只会在实际使用时才需要付费。</p>
<p>我不知道你怎么想的，但我认为这非常棒，几乎是神奇的！而且这是一个很好的、实用的方法。这是一种在你的工作流程中使用云计算/服务的好的、实用的方法。</p>
<p>当然，你可以定制这个流程，在前端使用 React 或 Vue 等框架，或使用 Python 或 Go 等不同的编程语言来实现 Lambda。</p>
<h2 id="">在你离开之前</h2>
<p>感谢你读到这里！我写的文章是关于 JavaScript、云计算开发，以及我作为一个自学成才的开发者的个人教育和职业经历。你可以在 Twitter 上关注我 <a href="https://twitter.com/adham_benhawy">@adham_benhawy</a>，我也会在 Twitter 上发表相关的文章！</p>
<h3 id="">资源</h3>
<ul>
<li><a href="https://aws.amazon.com/premiumsupport/knowledge-center/lambda-send-email-ses/">https://aws.amazon.com/premiumsupport/knowledge-center/lambda-send-email-ses/</a></li>
<li><a href="https://docs.aws.amazon.com/lambda/latest/dg/lambda-invocation.html">https://docs.aws.amazon.com/lambda/latest/dg/lambda-invocation.html</a></li>
<li><a href="https://docs.aws.amazon.com/lambda/latest/dg/services-apigateway.html?icmpid=docs_lambda_console">https://docs.aws.amazon.com/lambda/latest/dg/services-apigateway.html?icmpid=docs_lambda_console</a></li>
</ul>
<!--kg-card-end: markdown--> ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ AWS IAM 规则、访问角色和资源 ]]>
                </title>
                <description>
                    <![CDATA[ 原文：AWS IAM – Policy, Access Roles, Resources Explained, and Why They're Useful [https://www.freecodecamp.org/news/the-introduction-to-iam-i-wish-i-had/]，作者： Periklis Gkolias [https://www.freecodecamp.org/news/author/periklis-gkolias/] IAM，即身份和访问管理，是你在云原生环境中最常听到的术语之一。 但它是做什么的？如果你已经熟悉了 IAM，你花了多长时间才完全理解它？ 我将解释这个庞大的软件家族背后的主要概念，并考虑到你是个忙碌的工程师。 这里描述的基本原理与供应商无关，尽管我的大部分经验是在 AWS 的使用。 什么是 IAM? IAM 是一个由请求访问系统的实体（人类、应用程序等）组成的复杂系统。它也是一套分层的规则，用于授予或拒绝所请求的访问。 在我们进一步讨论之前，这里是你会遇到的主要术语：  * Resource，任何值得保护的东西 ]]>
                </description>
                <link>https://www.freecodecamp.org/chinese/news/the-introduction-to-iam-i-wish-i-had/</link>
                <guid isPermaLink="false">6270bba3395ec5063718b5ec</guid>
                
                    <category>
                        <![CDATA[ AWS ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ luojiyin ]]>
                </dc:creator>
                <pubDate>Tue, 03 May 2022 05:20:00 +0000</pubDate>
                <media:content url="https://chinese.freecodecamp.org/news/content/images/2022/05/g7In5Xr-2.jpeg" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>原文：<a href="https://www.freecodecamp.org/news/the-introduction-to-iam-i-wish-i-had/">AWS IAM – Policy, Access Roles, Resources Explained, and Why They're Useful</a>，作者：<a href="https://www.freecodecamp.org/news/author/periklis-gkolias/">Periklis Gkolias</a></p><!--kg-card-begin: markdown--><p>IAM，即身份和访问管理，是你在云原生环境中最常听到的术语之一。</p>
<p>但它是做什么的？如果你已经熟悉了 IAM，你花了多长时间才完全理解它？</p>
<p>我将解释这个庞大的软件家族背后的主要概念，并考虑到你是个忙碌的工程师。</p>
<p>这里描述的基本原理与供应商无关，尽管我的大部分经验是在 AWS 的使用。</p>
<h2 id="iam">什么是 IAM?</h2>
<p>IAM 是一个由请求访问系统的实体（人类、应用程序等）组成的复杂系统。它也是一套分层的规则，用于授予或拒绝所请求的访问。</p>
<p>在我们进一步讨论之前，这里是你会遇到的主要术语：</p>
<ul>
<li><strong>Resource</strong>，任何值得保护的东西，一个存储服务、虚拟机等</li>
<li><strong>Policy</strong>，一组规则，规定谁可以和谁不可以在单个资源或资源组上做什么</li>
<li><strong>Action</strong>，人可以在云环境中可以做事情，例如，创建一个虚拟机</li>
<li><strong>User</strong>，嗯，一个用户:)</li>
<li><strong>Group</strong>，一个具有相同权限的用户组</li>
<li><strong>Principal</strong>，一个请求访问的用户或应用程序</li>
<li><strong>Role</strong>，分配给一个委托人的一组权力，通常在有限的时间内</li>
</ul>
<h2 id="iam">为什么 IAM 是有用的</h2>
<p>IAM 主要用于认证、授权、细化访问和治理。</p>
<p>让我们看看这些都是什么意思：</p>
<ul>
<li><strong>Authentication</strong>，证实你是谁的行为</li>
<li><strong>Authorization</strong>，识别某人是否可以执行他们所请求的行动，这通常与认证相结合，但不一定</li>
<li><strong>Granular access</strong>，控制一个资源上可能发生的每个动作的权限，例如，一个用户可能有权限查看防火墙规则，但没有权限修改它们，这是用<a href="https://en.wikipedia.org/wiki/Role-based_access_control">基于角色的访问控制</a>实现的</li>
<li><strong>Governance</strong>，你为了解环境中发生的事情而采取的行动，主要是出于预算、合规性和适当的访问范围等原因</li>
</ul>
<p>如果你是一个只有 1-3 人的公司，那么建立一个完整的 IAM 解决方案可能是多余的。但是，如果你的团队规模大于这个数字，或者你打算扩大规模，那么你应该开始考虑这个问题。</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2022/04/WxyvyO4.jpg" alt="IAM pillars" width="600" height="400" loading="lazy"></p>
<h2 id="iam">你不使用 IAM 的常见问题</h2>
<p>我相信你可以看到 IAM 解决方案的好处。</p>
<p>现在让我们来看看企业在没有 IAM 的情况下面临的一些常见问题。</p>
<h3 id="">很难对访问进行审计和管理</h3>
<p>你是否听说过某个员工的权限超过了他们应该有的权限的情况？此外，没有人知道？</p>
<p>这可以通过正确设置的 IAM 解决方案来防止。</p>
<h3 id="">为新员工设置账户是件麻烦事</h3>
<p>有了 IAM 解决方案，这将只是一个点击几下的问题。也就是说，设置用户并将他们添加到他们团队使用的 IAM 组。就这样了。</p>
<p>但如果没有 IAM 解决方案呢？你将需要手动设置每个账户的所有权限。</p>
<p>你可能有一个参考用户来复制，但每个新账户是否需要参考用户的所有权限？你对少于 6 个月的用户账户有特殊处理吗？参考用户是否有超级用户的权限，避免意外地分配给新雇员账户？</p>
<h3 id="">处理离职人员很费时间</h3>
<p>在这里，你将遇到与上述新员工案例类似的问题。但是当同事离开时，你需要更改他们<strong>可能</strong>使用的所有帐户的密码。</p>
<p>这会很快变得麻烦，更不用说这对其他团队成员产生的副作用了。</p>
<p>每当有离职时，你都必须为每个脚本、应用程序和其他资源执行此操作。如果你每个月有 2-3 次团队变动怎么办？你和你的团队将很难高效工作。</p>
<h3 id="">简单的事情需要人去处理</h3>
<p>如果没有 IAM 解决方案，重设密码或重新启用被锁定的账户等任务就需要手动完成。</p>
<p>顶级的 IAM 解决方案有办法快速解决此类问题，而不需要太多的麻烦。</p>
<h2 id="">最佳实践</h2>
<p><img src="https://www.freecodecamp.org/news/content/images/2022/04/M7N8blv.jpg" alt="Best practices" width="600" height="400" loading="lazy"></p>
<p>如果你已经决定建立 IAM，这里有一些最佳实践。这远远不是一个完整的清单，而且是基于我的个人经验。但我在不止一个团队中看到过这些做法，所以它们应该也适用于你。</p>
<h3 id="">永远不要授予完全访问权</h3>
<p>在现实世界中，你不会希望每个用户都对一个账户有无限的访问权。理想情况下，没有人应该完全访问任何东西（除了账户所有者之外）。</p>
<p>例如，如果一个员工的职责是监控日志，他们应该对该工具只有阅读权限。他们不应该能够重新启动一项服务，或查看账单信息。</p>
<h3 id="">首选组而不是多个用户</h3>
<p>当你有选择时，最好使用组而不是多个用户。组使管理成倍地容易。</p>
<p>例如，如果一个新的人作为开发人员加入你的组织，他们可以被添加到一个开发人员的 IAM 组。然后，这个新人将继承该 IAM 组的所有权力。</p>
<p>另一种方法，即为每个组创建一个用户（reader_susan, admin_susan）被认为是过时的。</p>
<h3 id="">首选现有用户的角色而不是创建新用户</h3>
<p>如果有选择的话，最好是给现有的用户分配一个角色，而不是创建一个新的用户。</p>
<p>例如，不要创建一个管理员用户并在 10 个人之间分享密码。创建一个管理员角色，并在有限的时间内将其分配给需要它的人。</p>
<h3 id="">经常审计权限</h3>
<p>这很容易犯错或进行恶意操作。至少，公司应该定期审计权限，并确保只有适当的人拥有其角色所需的最低级别的访问权限。</p>
<p>你也可以在可疑的行动发生时向某个团队发送电子邮件。例如，将管理员角色分配给一个新雇员。</p>
<h3 id="">预先设置边界</h3>
<p>如果 IAM 解决方案允许，请将边界添加到你的生态系统中。</p>
<p>根据亚马逊的文档：</p>
<blockquote>
<p>权限边界（permissions boundary）是一种高级功能，用于使用管理策略来设置基于身份的策略可以授予 IAM 实体的最大权限。一个实体的权限边界允许它只执行其基于身份的策略和其权限边界所允许的行动。</p>
</blockquote>
<p>（我知道，我知道——我答应过要与供应商无关的🙂 ）</p>
<p>用通俗的话说，你可以定义可以分配给任何人的 <code>最大</code> 权限。</p>
<p>例如，一个用户最多只能从相关工具中查看日志，并重新启动一项服务。如果有人试图让一个角色创建一个新的虚拟机，他们将被禁止。</p>
<h2 id="">结论</h2>
<p>谢谢你读到这里。我希望你喜欢这个关于 IAM 的介绍。</p>
<p>如果你有任何问题，请在 <a href="https://twitter.com/PeriGk_Tech">Twitter</a> 上与我联系。</p>
<!--kg-card-end: markdown--> ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ 如何使用 GitHub Actions 将 Next.js 网站部署到 AWS S3 ]]>
                </title>
                <description>
                    <![CDATA[ Next.js和静态 web应用的好处是，你将它们储存在对象储存里，几乎可以在任何地方运行, 比如AWS S3。但是每次手动更新这些文件可能是一件痛苦的事. 我们如何用Github Actions来自动持续部署我们的应用程序到S3?  * 什么是GitHub Actions  * 什么是持续部署  * 我们怎么去构建  * 第0步: 在GitHub上建立一个新的Next.js项目  * 第1步: 手工创建一个用于部署next.js项目的新S3桶  * 第2步: 创建一个新的GitHub Action工作流来自动化构建一个Next.js项目  * 第3步: 配置一个GitHub Action，部署静态网站到S3上 什么是GitHub Actions GitHub Actions是GitHub的一项免费服务，它允许我们用代码实现任务自动化。 我在这篇文章 [https://www.freecodecamp.org/news/what-are-github-actions-and-how-can-you-automate-tests-and-slack-notifications/] ]]>
                </description>
                <link>https://www.freecodecamp.org/chinese/news/how-to-use-github-actions-to-deploy-a-next-js-website-to-aws-s3/</link>
                <guid isPermaLink="false">61dfafe26161280665ed8021</guid>
                
                    <category>
                        <![CDATA[ NextJS ]]>
                    </category>
                
                    <category>
                        <![CDATA[ AWS ]]>
                    </category>
                
                    <category>
                        <![CDATA[ GitHub ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ luojiyin ]]>
                </dc:creator>
                <pubDate>Thu, 13 Jan 2022 04:30:00 +0000</pubDate>
                <media:content url="https://chinese.freecodecamp.org/news/content/images/2022/01/actions-s3.jpeg" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>Next.js和静态 web应用的好处是，你将它们储存在对象储存里，几乎可以在任何地方运行, 比如AWS S3。但是每次手动更新这些文件可能是一件痛苦的事.</p>
<p>我们如何用Github Actions来自动持续部署我们的应用程序到S3?</p>
<ul>
<li>什么是GitHub Actions</li>
<li>什么是持续部署</li>
<li>我们怎么去构建</li>
<li>第0步: 在GitHub上建立一个新的Next.js项目</li>
<li>第1步: 手工创建一个用于部署next.js项目的新S3桶</li>
<li>第2步: 创建一个新的GitHub Action工作流来自动化构建一个Next.js项目</li>
<li>第3步: 配置一个GitHub Action，部署静态网站到S3上</li>
</ul>
<h2 id="githubactions">什么是GitHub Actions</h2>
<p>GitHub Actions是GitHub的一项免费服务，它允许我们用代码实现任务自动化。</p>
<p>我在<a href="https://www.freecodecamp.org/news/what-are-github-actions-and-how-can-you-automate-tests-and-slack-notifications/">这篇文章</a> 里介绍了如何用它来自动化任务，比如在运行代码中的测试，并向Slack发送通知。</p>
<p>它们提供一种灵活的方式，在我们现有的工作流基础上为自动化运行代码。这提供了很多的可能性，比如部署我们的网站。</p>
<h2 id="awss3">什么是 AWS S3？</h2>
<p><a href="https://aws.amazon.com/s3/">S3</a>（简单存储服务）是AWS的一个对象存储服务。它允许你在云上轻松存储文件，使它们在世界各地都可以使用。</p>
<p>它还允许你将这些文件作为一个网站使用。因为我们可以把HTML文件作为一个对象上传，我们也可以配置S3，让一个HTTP请求访问该文件。 这意味着，我们可以<a href="https://www.freecodecamp.org/news/how-to-host-and-deploy-a-static-website-or-jamstack-app-to-s3-and-cloudfront/">在S3中直接托管整个网站</a>.</p>
<h2 id="">什么使持续部署？</h2>
<p>持续部署（Continuous Deployment），通常是指将代码处在可发布的状态，自动化部署代码，缩短部署部署时间。</p>
<p>在这个示例中，我们将配置项目，持续将更新推送或者合并到git主分支，部署到网站。</p>
<h2 id="">我们怎样去构建?</h2>
<p>我们首先要使用默认的Next.js起始模板初始化一个简单的<a href="https://nextjs.org/">Next.js</a>应用，并配置将其编译成静态文件。</p>
<p>如果你不向创建一个Next.js项目，你甚至用一个简单的HTML文件跟着做，并不运行任何构建命令。但Next.js是构建动态网站应用的一种现代化方式，所以我将从这里开始。</p>
<p>随着我们的网站文件准备就绪，我们将在AWS中创建和配置一个S3桶，在S3桶上托管我们的网站。</p>
<p>最后，我们将创建一个新的GitHub Action工作流，当我们的主分支（<code>main</code>）发生新的变化时，它将自动更新S3中网站文件。</p>
<h2 id="0githubnextjs">第0步：在GitHub上创建一个新的Next.js项目</h2>
<p>我们将从Next.js的默认模板开始。</p>
<p>在创建你像创建项目的目录后，运行:</p>
<pre><code>yarn create next-app my-static-website
# 或者
npx create-next-app my-static-website
</code></pre>
<p>注意： 请注意将<code>my-static-website</code>替换为你想选择的名称。我们将在本教程的其余部分使用这个名字。</p>
<p>如果进入到该目录并运行开发命令，你应该能够成功启动你的开发服务器。</p>
<pre><code>cd my-static-website
yarn dev
# or
npm run dev
</code></pre>
<p><img src="https://www.freecodecamp.org/news/content/images/2020/10/new-nextjs-app.jpg" alt="new-nextjs-app" width="600" height="400" loading="lazy"></p>
<p>New Next.js App</p>
<p>接下来，让我们把我们的项目配置为静态编译。</p>
<p>在 <code>package.json</code>文件, 把<code>build</code> 脚本改为 :</p>
<pre><code class="language-json">"build": "next build &amp;&amp; next export",
</code></pre>
<p>这样做的目的时告诉Next将网站导出为静态文件，我们将它用于托管网站。</p>
<pre><code>yarn build
# 或者
npm run build
</code></pre>
<p>一旦完成, 我们将查看 <code>out</code>目录，看到我们新网站的所有文件。</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2020/10/nextjs-build-export-output.jpg" alt="nextjs-build-export-output" width="600" height="400" loading="lazy"></p>
<p>Next.js 的静态输出</p>
<p>最后，我们要把它推送到GitHub上。</p>
<p>在你的GitHub账号中 <a href="https://docs.github.com/en/free-pro-team@latest/github/getting-started-with-github/create-a-repo">创建一个新的仓库</a>。然后会提供如何 <a href="https://docs.github.com/en/free-pro-team@latest/github/importing-your-projects-to-github/adding-an-existing-project-to-github-using-the-command-line">添加现有项目</a>到该仓库的说明。</p>
<p>一旦把你的项目推送到GitHub上，我们就做好了建立我的新网站项目的准备。</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2020/10/project-on-github.jpg" alt="project-on-github" width="600" height="400" loading="lazy"></p>
<p>GitHub中的新repo</p>
<p>有下面的提交内容</p>
<ul>
<li><a href="https://github.com/colbyfayock/my-static-website/commit/ca9e4bca3c37fbd8553b0b183890c32836c35296">添加初始Next.js项目</a> 通过 <a href="https://nextjs.org/docs/api-reference/create-next-app">创建Next引用</a></li>
<li><a href="https://github.com/colbyfayock/my-static-website/commit/7907f4a0fac5f0aed2922202c5f0070dfc055f83">配置Next.js导出</a></li>
</ul>
<h2 id="1s3nextjs">第1步: 手动创建新的S3桶，并将Next.js项目部署到上面。</h2>
<p>要开始使用我们的新S3桶，首先登录你的AWS账号，并进入到S3服务。</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2020/10/aws-s3-console.jpg" alt="aws-s3-console" width="600" height="400" loading="lazy"></p>
<p>发现没有桶</p>
<p>我们要创建一个新桶，使用我们选择的名字命名，用于我们网址托管的S3，我们还要配置我们的S3桶，使其能够托管一个网站。</p>
<p>_注意: 本教程步会指导你如何在S3上托管网站，但是你可以查看我的另一个教程，该教程将一步步地 <a href="https://www.freecodecamp.org/news/how-to-host-and-deploy-a-static-website-or-jamstack-app-to-s3-and-cloudfront/">指导你在S3上托管网站</a>。</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2020/10/s3-bucket-website-hosting.jpg" alt="s3-bucket-website-hosting" width="600" height="400" loading="lazy"></p>
<p>静态网站在AWS S3上托管</p>
<p>当我们把S3桶配置成一个网站，我们就可以回到Next.js项目文件夹，运行我们的构建命令，然后把<code>out</code>文件夹中的所有文件上传到我们的新建的S3桶。</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2020/10/website-files-in-s3.jpg" alt="website-files-in-s3" width="600" height="400" loading="lazy"></p>
<p>S3桶上的静态应用</p>
<p>当这些文件被上传，并且我们已经为网站托管配置了S3桶，我们现在应该能看到我们的项目在网络上线运行！</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2020/10/nextjs-s3-website.jpg" alt="nextjs-s3-website" width="600" height="400" loading="lazy"></p>
<p>AWS S3托管Next.js应用程序</p>
<h2 id="2githubactionnextjs">第2步: 创建一个新的GitHub Action工作流来自动构建一个Next.js项目</h2>
<p>首先，我们需要创建一个新的工作流程(workflow)。</p>
<p>如果你熟悉GitHub Actions，你可以手动创建一个，单我们将通过用户界面快速创建一个。</p>
<p>进入GitHub的仓库中的<code>Action</code>标签，点击<code>set up a workflow yourself</code>,来自行设置工作流。</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2020/10/github-actions-new-workflow.jpg" alt="github-actions-new-workflow" width="600" height="400" loading="lazy"></p>
<p>新的GitHub Action工作流</p>
<p>GitHub提供了一个模板，我们可以在工作流程中使用，不过我们要做一些修改。</p>
<p>让我们做以下工作。</p>
<ul>
<li>可选: 将文件重名为deploy.yml</li>
<li>可选: 将workflow重名为CD (因为它与CI不同)</li>
<li>可选: 删除所有的注释，使其更容易阅读</li>
<li>删除<code>on</code> 属性中的<code>pull_request</code></li>
<li>删除所有的 <code>steps</code> 除了<code>uses: actions/checkout@v2</code></li>
</ul>
<p>因此，在这一点上，我们应该剩下的是:</p>
<pre><code class="language-yaml">name: CD

on:
  push:
    branches: [ main ]

jobs:
  build:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v2
</code></pre>
<p>仅仅这段代码会触发一个流程，会启动一个新的Ubuntu实例，并在GitHub上有新的改动推送到主分支后，拉取代码到Ubuntu上。</p>
<p>接下来， 当我们获取我们的代码后，我们要构建它。然后将输出文件同步到S3。</p>
<p>这一步将基于你的项目使用yarn还是npm有所不同。</p>
<p>如果你使用yarn，在 <code>steps</code>定义下，添加以下内容。</p>
<pre><code class="language-yaml">- uses: actions/setup-node@v1
  with:
    node-version: 12
- run: npm install -g yarn
- run: yarn install --frozen-lockfile
- run: yarn build
</code></pre>
<p>如果是使用npm，添加以下内容:</p>
<pre><code class="language-yaml">- uses: actions/setup-node@v1
  with:
    node-version: 12
- run: npm ci
- run: npm run build
</code></pre>
<p>在这两个步骤之间，我们要做的是:</p>
<ul>
<li>设置 node: 这是为了我们能够使用npm 和node 来安装和运行的脚本</li>
<li>安装Yarn (仅对使用Yarn): 如果我们使用Yarn，我们将为其安装全局依赖，以便我们使用它</li>
<li>安装依赖: 我们安装我们的依赖，我们使用一个特定命令，确保我们使用<code>lock</code>文件，以避免任何意外的软件包升级</li>
<li>构建: 最后, 我们运行我们的构建命令，将我们的Next.js项目编译到<code>out</code>目录中。</li>
</ul>
<p>现在我们可以将该该文件直接提交到我们的<code>main</code>分支，这触发我们的workflow的运行，我们可以子啊<code>Actions</code>标签里看到。</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2020/10/github-action-run-workflow.jpg" alt="github-action-run-workflow" width="600" height="400" loading="lazy"></p>
<p>在GitHub Actions中新的workflow</p>
<p>为了看到它的运行状态，我们进入运行的<code>workflow</code>，选择我们的<code>workflow</code>，看到所有我们的项目包含的步骤。</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2020/10/github-action-successful-build.jpg" alt="github-action-successful-build" width="600" height="400" loading="lazy"></p>
<p>GitHub Action成功构建日志</p>
<p><a href="https://github.com/colbyfayock/my-static-website/commit/59e0a5158d6afbf54793d826d05455f5205c98fb">随着提交!</a></p>
<h2 id="3githubactions3">第3步: 配置一个GitHub Action，将静态网站部署到S3上</h2>
<p>现在我们正在自动构建我们的项目，我们想在S3中自动更新我们的网站。</p>
<p>为了做到这一点，我们将使用GitHub Action <a href="https://github.com/aws-actions/configure-aws-credentials">aws-actions/configure-aws-credentials(配置aws凭证)</a> 和 the AWS CLI(AWS提供的命令行)。</p>
<p>我们使用GitHub Action 将接收我们的AWS凭证和配置，并在workflow的生命周期内使用。</p>
<p>目前，GitHub Action中的Ubuntu实例允许使用AWS CLI，因为它包含在其中。因此，我们将能够在workflow中使用CLI命令。</p>
<p>另外，我们也可以使用<a href="https://github.com/jakejarvis/s3-sync-action">S3 Sync action</a>。但是通过使用AWS CLI，我们可以获得更多的灵活性来定制我们的设置，我们可以使用它来获得额外的CLI命令，一般来说，熟悉AWS CLI也是不错的。</p>
<p>为了开始，让我们在workflow添加以下片段作为附加步骤。</p>
<pre><code class="language-yaml">- uses: aws-actions/configure-aws-credentials@v1
  with:
    aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }}
    aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
    aws-region: us-east-1
</code></pre>
<p>上面要做的是使用AWS凭证配置action，根据我们的设置来设置我们的AWS的Access Key和Secret Key还有region(区域)。</p>
<p>AWS Region可以自定义为你通常使用的AWS账号的任何区域，我在美国东北部，所以我设置为<code>us-east-1</code>。</p>
<p>Access Key和Secret Key是你需要你的AWS账号生成的凭证。我们的代码设置方式是，我们将这些值存储在GitHub Secrets里，要防止这些密钥被泄。当action运行时，GitHub会将这些值改为星星(<code>***</code>)，这样人们就无法访问这些密钥。</p>
<p>为了设置这些secrets,我们首先要在AWS生成 <code>Access Keys</code>。</p>
<p>进入了AWS控制台。在用户菜单下，选择 <strong>My Security Credentials</strong>，然后选择 <strong>Create access key</strong>。</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2020/10/aws-console-create-access-key.jpg" alt="aws-console-create-access-key" width="600" height="400" loading="lazy"></p>
<p>在AWS创建一个 <code>Access Key</code></p>
<p>这会生成两个值  <strong>Access key ID</strong> 和<strong>Secret access key</strong>。必须保存好这些值，因为你将无法再次访问<code>Secret key ID </code>。</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2020/10/aws-secret-access-keys.jpg" alt="aws-secret-access-keys" width="600" height="400" loading="lazy"></p>
<p>在AWS中寻找 <code>Secret Key</code> 和 <code>Access Key</code></p>
<p><em>注意: 记住不要再你的代码中包含<code>Access Key</code>和<code>Secret Key</code>。这可能导致有人破坏你的AWS凭证。</em></p>
<p>下一步，在GitHub repo中，进入到 Settings -&gt; Secrets，然后选择 <code>New secret</code>。</p>
<p>在这里，我们要使用AWS keys添加到下面的secrets:</p>
<ul>
<li>AWS_ACCESS_KEY_ID: your AWS Access key ID</li>
<li>AWS_SECRET_ACCESS_KEY: your AWS Secret key</li>
</ul>
<p>当保存下来，你就应该记住这两个新的<code>secrets</code>。</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2020/10/github-secrets-access-keys.jpg" alt="github-secrets-access-keys" width="600" height="400" loading="lazy"></p>
<p>在GitHub中创建<code>Secrets</code></p>
<p>现在我们已经配置好了我们的凭证，我们应该为运行命令，将我们的项目同步到S3，做好准备。</p>
<p>在Github Action，添加以下步骤:</p>
<pre><code class="language-yaml">- run: aws s3 sync ./out s3://[bucket-name]
</code></pre>
<p><em>注意: 请确保<code>[bucket-name]</code> 替换为你的S3桶的名称。</em></p>
<p>这个命令会触发与我们的S3桶的同步(sync)，使用<code>out</code>目录的文件，也就是我们项目构建的地方。</p>
<p>现在，如果我们提交我们的修改，我们可以看到，一旦提交到<code>main</code>分支，我们的actions会自动触发，我们构建我们的项目并同步到S3！</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2020/10/github-action-sync-s3-bucket.jpg" alt="github-action-sync-s3-bucket" width="600" height="400" loading="lazy"></p>
<p>成功通过GitHub Action workflow 同步到AWS S3</p>
<p><em>注意: 请确保在设置这个action之前，你已经将S3桶配置为网站托管(包括解除S3桶权限) --否则这个action可能失败。</em></p>
<p>在这一点上，我们的项目可能看起来是一样的，因为我们对代码进行任何修改。</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2020/10/nextjs-s3-website.jpg" alt="nextjs-s3-website" width="600" height="400" loading="lazy"></p>
<p>AWS S3的Next.js应用程序</p>
<p>但如果你做了一个代码修改，比如在<code>pages/index.js</code>中改变主页的标题，并提交该修改:</p>
<pre><code class="language-jsx">&lt;h1 className={styles.title}&gt;
  Colby's &lt;a href="https://nextjs.org"&gt;Next.js!&lt;/a&gt; Site
&lt;/h1&gt;
</code></pre>
<p>我们可以看到，我们的修改触发了workflow的启动:</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2020/10/github-action-commit-workflow.jpg" alt="github-action-commit-workflow" width="600" height="400" loading="lazy"></p>
<p>新的GitHub Action workflow的触发来自代码改变</p>
<p>一旦我们的workflow完成，我们可以看到我们的内容已经在我们的网站上自动更新。</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2020/10/updated-nextjs-site-title.jpg" alt="updated-nextjs-site-title" width="600" height="400" loading="lazy"></p>
<p>AWS S3托管的应用程序，代码已经更新</p>
<p>随着内容的提交</p>
<ul>
<li><a href="https://github.com/colbyfayock/my-static-website/commit/f891412b827aca4b06e9bf3de8e4e5b4c5704fc8">添加ASW的配置和S3 sync命令</a></li>
<li><a href="https://github.com/colbyfayock/my-static-website/commit/bb9b981416645e35c6d3442e02d6b61f2ba032d2">测试workflow的标题的更新</a></li>
</ul>
<h2 id="">我们还能做什么?</h2>
<h3 id="cloudfront">设置CloudFront</h3>
<p>这个篇文章的目的不是要经历AWS配置网站的整个过程，但是你在S3上运行网站服务，你可能在之前考虑过CloudFront。</p>
<p>你可以查看以下<a href="https://www.freecodecamp.org/news/how-to-host-and-deploy-a-static-website-or-jamstack-app-to-s3-and-cloudfront/">我的另一个指南</a>，它指导你如何设置CloudFront，以及如何在S3中创建网站的手把手指南。</p>
<h3 id="cloudfront">CloudFront的缓存失效</h3>
<p>如果你的S3网站在CloudFront后面，有可能你会确保CloudFront没有缓存新的变化。</p>
<p>通过AWS CLI，我们也可以触发CloudFront的缓存失效，以确保它正在抓取最新的变化。</p>
<p><a href="https://docs.aws.amazon.com/cli/latest/reference/cloudfront/create-invalidation.html">请看这里的文档</a>学习更多的知识.</p>
<h3 id="pullrequest">pull request部署</h3>
<p>如果你不断地在pull request中的网站修改，有时候更容易看到网站的修改。</p>
<p>你可以设置一个新的workflow，只在pull request上运行，workflow可以根据分支或者环境动态创建一个新的桶，并在pull request上添加一个带有该URL的comment。</p>
<p>你也许能找到一个GitHub Action 作为你管理你pull request上带的comments,你可以查询<a href="https://docs.github.com/en/free-pro-team@latest/rest/reference/actions">GitHub Actions文档</a>.</p>
<p><a href="https://twitter.com/colbyfayock"><img src="https://res.cloudinary.com/fay/image/upload/w_2000,h_400,c_fill,q_auto,f_auto/w_1020,c_fit,co_rgb:007079,g_north_west,x_635,y_70,l_text:Source%20Sans%20Pro_64_line_spacing_-10_bold:Colby%20Fayock/w_1020,c_fit,co_rgb:383f43,g_west,x_635,y_6,l_text:Source%20Sans%20Pro_44_line_spacing_0_normal:Follow%20me%20for%20more%20JavaScript%252c%20UX%252c%20and%20other%20interesting%20things!/w_1020,c_fit,co_rgb:007079,g_south_west,x_635,y_70,l_text:Source%20Sans%20Pro_40_line_spacing_-10_semibold:colbyfayock.com/w_300,c_fit,co_rgb:7c848a,g_north_west,x_1725,y_68,l_text:Source%20Sans%20Pro_40_line_spacing_-10_normal:colbyfayock/w_300,c_fit,co_rgb:7c848a,g_north_west,x_1725,y_145,l_text:Source%20Sans%20Pro_40_line_spacing_-10_normal:colbyfayock/w_300,c_fit,co_rgb:7c848a,g_north_west,x_1725,y_222,l_text:Source%20Sans%20Pro_40_line_spacing_-10_normal:colbyfayock/w_300,c_fit,co_rgb:7c848a,g_north_west,x_1725,y_295,l_text:Source%20Sans%20Pro_40_line_spacing_-10_normal:colbyfayock/v1/social-footer-card" alt="关注我，了解更多的Javascript、UX和其他有趣的事情!" width="2000" height="400" loading="lazy"></a></p>
<ul>
<li><a href="https://twitter.com/colbyfayock">🐦 在推特上关注我</a></li>
<li><a href="https://youtube.com/colbyfayock">🎥 在油管上订阅我</a></li>
<li><a href="https://www.colbyfayock.com/newsletter/">✉️ 订阅我的Newsletter</a></li>
<li><a href="https://github.com/sponsors/colbyfayock">💝 赞助我</a></li>
</ul>
<!--kg-card-end: markdown--><p>原文：<a href="https://www.freecodecamp.org/news/how-to-use-github-actions-to-deploy-a-next-js-website-to-aws-s3/">How to Use Github Actions to Deploy a Next.js Website to AWS S3</a>，作者：<a href="https://www.freecodecamp.org/news/author/colbyfayock/">Colby Fayock</a></p> ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ 使用 WebSockets，AWS API Gateway 和 Lambda 创建实时应用程序 ]]>
                </title>
                <description>
                    <![CDATA[ 最近 AWS 宣布推出了一项广受欢迎的功能：Amazon API Gateway 的 WebSockets。通过 WebSockets，可以构建双向通讯，用于一些诸如实时应用程序的场景。那么问题来了，什么是实时应用程序呢？我们先来解答这个问题。 目前大部分的应用都是客户端 - 服务器架构。在这种架构中，客户端使用网络通信通过 Internet 发送请求，然后服务器处理该请求并将响应返回给客户端。 客户端 - 服务器架构中客户端是通信的发起方，首先，客户端发起通讯，然后服务器返回服务生成的请求。如果服务端想直接向客户端推送消息，而不是被动的响应客户端的请求该怎么办呢？实时应用程序的用武之地就到啦。 实时应用程序就是服务器无需客户端请求数据就可以直接向客户端推送消息的应用程序。假设我们有一个聊天应用程序，客户端之间可以通过服务器进行通信。如果客户端通过轮询的方式每秒向服务器发送请求判断是否有新消息，会增加服务器的成本。更有效率的做法是当客户端需要收消息时服务器直接推送消息给客户端。这个功能可以通过实时应用程序来完成。 在 2018 AWS re:Invent 上 Amazon 宣布 ]]>
                </description>
                <link>https://www.freecodecamp.org/chinese/news/real-time-applications-using-websockets-with-aws-api-gateway-and-lambda/</link>
                <guid isPermaLink="false">602758196183a70540156884</guid>
                
                    <category>
                        <![CDATA[ AWS ]]>
                    </category>
                
                    <category>
                        <![CDATA[ WebSocket ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Lambda ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ ZhichengChen ]]>
                </dc:creator>
                <pubDate>Fri, 12 Feb 2021 04:39:00 +0000</pubDate>
                <media:content url="https://chinese.freecodecamp.org/news/content/images/2021/02/0_OaDVOjdkCturioO_.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>最近 AWS 宣布推出了一项广受欢迎的功能：Amazon API Gateway 的 WebSockets。通过 WebSockets，可以构建双向通讯，用于一些诸如实时应用程序的场景。那么问题来了，什么是实时应用程序呢？我们先来解答这个问题。</p>
<p>目前大部分的应用都是客户端 - 服务器架构。在这种架构中，客户端使用网络通信通过 Internet 发送请求，然后服务器处理该请求并将响应返回给客户端。</p>
<p>客户端 - 服务器架构中客户端是通信的发起方，首先，客户端发起通讯，然后服务器返回服务生成的请求。如果服务端想直接向客户端推送消息，而不是被动的响应客户端的请求该怎么办呢？实时应用程序的用武之地就到啦。</p>
<p>实时应用程序就是服务器无需客户端请求数据就可以直接向客户端推送消息的应用程序。假设我们有一个聊天应用程序，客户端之间可以通过服务器进行通信。如果客户端通过轮询的方式每秒向服务器发送请求判断是否有新消息，会增加服务器的成本。更有效率的做法是当客户端需要收消息时服务器直接推送消息给客户端。这个功能可以通过实时应用程序来完成。</p>
<p>在 2018 AWS re:Invent 上 Amazon 宣布将在 API Gateway 中支持 WebSockets。12 月下旬，API Gateway 正式支持了 WebSockets。也就是说，现在通过 AWS 的基础设施就可以创建一个实时应用程序。</p>
<p>在本文中，我们将使用 API Gateway WebSockets 创建一个简单的聊天应用程序。在开始之前，需要了解一些关于实时应用程序和 API Gateway 的概念。</p>
<h2 id="websocketapi">WebSocket API 概念</h2>
<p>WebSocket API 由一个或多个路由组成。请求通过路由表达式选择的特定路由，表达式根据请求匹配与路由的 <code>routeKey</code> 相对应的值。比如，如果请求的 JSON 消息包含 <code>action</code> 属性，想要根据不同的 action 执行不同的操作，那么可以通过 <code>${request.body.action}</code> 路由表达式来匹配路由执行相应的操作。</p>
<p>比如，如果请求的 JSON 消息为 <code>{"action": "onMessage", message: "Hello everyone"}</code>，那么这个请求将会匹配 <code>onMessage</code> 路由。</p>
<p>默认情况下，WebSocket API 中已经定义了三个路由。除了下面提到的三个路由外，还可以根据需要定制路由。</p>
<ul>
<li><strong>$default</strong> - 当路由表达式匹配不到 API 中的其它路由时，会默认使用这个路由。可以用于实现错误处理。</li>
<li><strong>$connect</strong> - 客户端第一次连接到 WebSocket API 时关联的路由。</li>
<li><strong>$disconnect</strong> - 客户端从 API 断开连接时关联的路由。</li>
</ul>
<p>通过 WebSocket API 成功连接客户端后，将为该客户端分配唯一的 connection id。一旦建立连接，connection id 将在整个生命周期内保持不变。向客户端推送消息时，需要发送带有 connection id 的 <code>POST</code> 请求，如下。</p>
<pre><code class="language-apl">POST https://{api-id}.execute-api.us-east 1.amazonaws.com/{stage}/@connections/{connection_id}
</code></pre>
<h2 id="">实现聊天室应用</h2>
<p>在学习了 WebSocket API 的基本概念之后，来看一下如何使用 WebSocket API 创建实时应用程序。在本文中，我们将使用 WebSocket API、AWS Lambda 和 DynamoDB 实现一个简单的聊天应用程序。下图展示了实时应用程序的架构。</p>
<p><img src="https://cdn-media-1.freecodecamp.org/images/EiHc2HzJkq621fqsah0gFRGo6J0298tfu5KF" alt="EiHc2HzJkq621fqsah0gFRGo6J0298tfu5KF" width="600" height="400" loading="lazy"></p>
<p>在我们的应用中，客户端将连接到 API Gateway。当客户端连接后，lambda 函数会将 connection id 保存在 DynamoDB 表中。在这里我们还需要向客户端推送消息，另一个 lambda 函数会检索需要发送客户端的 connection id，然后通过向 callback URL 发送 POST 请求将数据推送到客户端。</p>
<h3 id="websocketapi">创建 WebSocket API</h3>
<p>创建 WebSocket API，首先需要进入 Amazon API Gateway 服务控制台。选择 Create new API。单击 WebSocket 创建一个 WebSocket API，指定 API name 和路由选择表达式（Route Selection Expression）。在这里，添加 <code>${request.body.action}</code> 路由表达式，点击 Create API。</p>
<p><img src="https://cdn-media-1.freecodecamp.org/images/Fu3WT1nNu67o1AEQbVvyqPjF4Xfgdy5DIdU8" alt="Fu3WT1nNu67o1AEQbVvyqPjF4Xfgdy5DIdU8" width="600" height="400" loading="lazy"></p>
<p>创建 API 后，会重定向到路由页面。在这里可以看到三个预定义的路由：<code>$connect</code>、<code>$disconnect</code> 和 <code>$default</code>。在我们的架构中，<code>$connect</code> 和 <code>$disconnect</code> 路由完成以下任务：</p>
<ul>
<li><strong>$connect</strong> - 当调用此路由时，Lambda 函数会将客户端的  connection id 添加到 DynamoDB。</li>
<li><strong>$disconnect</strong> - 当调用此路由时 ，Lambda 函数会从 DynamoDB 中删除断开连接的客户端的 connection id。</li>
<li><strong>onMessage</strong> - 当调用此路由时，消息会发送到当前已经连接的所有的客户端。</li>
</ul>
<p>在根据上述步骤添加路由之前，我们还需要完成如下四个任务：</p>
<ul>
<li>创建一个 DynamoDB 表</li>
<li>创建一个 lambda connect 函数</li>
<li>创建一个 lambda disconnect 函数</li>
<li>创建一个 lambda onMessage 函数</li>
</ul>
<p>首先，来创建 DynamoDB 表。进入 DynamoDB 控制台创建一个名为 Chat 的新表。添加主键 'connectionid'。</p>
<p><img src="https://cdn-media-1.freecodecamp.org/images/tC0qYzoiulwhYS3pdneXVn1uzu927lCTm2Mz" alt="tC0qYzoiulwhYS3pdneXVn1uzu927lCTm2Mz" width="600" height="400" loading="lazy"></p>
<p>接下来，创建一个  Lambda connect 函数。进入 Lambda 控制台点击 create function。选择 Author from Scratch，指定名字 <code>ChatRoomConnectFunction</code> 以及含有必需的权限的 role。（role 应该具有从 DynamoDB get、put 和 delete 项目的权限，以及通过 API gateway 调用 API 的权限。）</p>
<p>在 lambda 函数的代码区域添加如下代码。这个代码将会添加连接客户端的 connection id 到我们刚刚创建的 DynamoDB 表中。</p>
<pre><code class="language-javascript">exports.handler = (event, context, callback) =&gt; {
     const connectionId = event.requestContext.connectionId;
     addConnectionId(connectionId).then(() =&gt; {
         callback(null, {statusCode: 200,})
     });
}

function addConnectionId(connectionId) {
    return ddb.put({
        TableName: 'Chat',
        Item: {
            connectionid : connectionId
        },
    }).promise();
}
</code></pre>
<p>接下来，创建一个 Lambda disconnect 函数。按照上面的步骤创建一个名为 <code>ChatRoomDisconnectionFunction</code> 的函数。函数代码如下。代码会在客户端断开连接时从 DynamoDB 表中删除 connection id。</p>
<pre><code class="language-javascript">const AWS = require('aws-sdk');
const ddb = new AWS.DynamoDB.DocumentClient();

exports.handler = (event, context, callback) =&gt; {
    const connectionId = event.requestContext.connectionId;
    addConnectionId(connectionId).then(() =&gt; {
        callback(null, {statusCode: 200,})
    });
}

function addConnectionId(connectionId) {
    return ddb.delete({
        TableName: 'Chat',
        Key: {
            connectionid : connectionId,
        },
    }).promise();
}
</code></pre>
<p>现在我们已经创建了一个 DynamoDB 表和两个 lambda 函数。在创建第三个函数之前，让我们回到 API Gateway 控制台使用刚刚创建的 lambda 函数来配置路由。首先，点击 <code>$connect</code> 路由，集成类型（ ingegration type）选择 Lambda 函数然后选择 ChatRoomConnectionFunction。</p>
<p><img src="https://cdn-media-1.freecodecamp.org/images/Jm4gDZE2iTvkM7jjEIbD5dJwxMpfQqNJcxaw" alt="Jm4gDZE2iTvkM7jjEIbD5dJwxMpfQqNJcxaw" width="600" height="400" loading="lazy"></p>
<p>同样使用 <code>ChatRoomDisconnectionFunction</code> Lambda 函数配置 <code>$disconnect</code> 路由：</p>
<p><img src="https://cdn-media-1.freecodecamp.org/images/xydcct1RcCF4MMgATbCUE7RoOlVw5Zx-rBzy" alt="xydcct1RcCF4MMgATbCUE7RoOlVw5Zx-rBzy" width="600" height="400" loading="lazy"></p>
<p>现在已经配置了 <code>$connect</code> 和 <code>$disconnect</code> 路由，已经可以测试一下 WebSocket API 了。测试之前需要部署 API。在 Actions 按钮的下拉选项里，点击 Deploy API 来发布。指定一个 stage name 如 Test 表明我们发布 API 的目的是为了测试。</p>
<p><img src="https://cdn-media-1.freecodecamp.org/images/Bflmr7zIfPBtVbdcQw-FhWrdWLomxD5wSMIu" alt="Bflmr7zIfPBtVbdcQw-FhWrdWLomxD5wSMIu" width="600" height="400" loading="lazy"></p>
<p>部署之后，会看到两个 URL。第一个是 WebSocket URL，第二个是 Connection URL。</p>
<p><img src="https://cdn-media-1.freecodecamp.org/images/81K7bxiFXv-JMJx8b5jScxBmnjkKM4brxY9Q" alt="81K7bxiFXv-JMJx8b5jScxBmnjkKM4brxY9Q" width="600" height="400" loading="lazy"></p>
<p>WebSocket URL 是客户端通过 WebSocket 连接到 API 的 URL。第二个 URL，即 Connection URL，是接下来用于向已连接客户端推送消息的 URL。由于还没有配置客户端推送，目前可以仅测试 <code>$connect</code> 和 <code>$disconnect</code> 路由。</p>
<p>为了便于测试 WebSocket，可以使用 wscat 工具。在命令行中执行 <code>npm  install  -g wscat</code> 命令来安装它。安装成功后就可以使用了。执行如下命令，连接到 WebScoket API，注意使用自己的 URL 替换 WebSocket URL。</p>
<pre><code class="language-bash">wscat -c wss://bh5a9s7j1e.execute-api.us-east-1.amazonaws.com/Test
</code></pre>
<p><img src="https://cdn-media-1.freecodecamp.org/images/8uYGb6iG04XmfBsGOWxhLsxAenVIafGSWRE3" alt="8uYGb6iG04XmfBsGOWxhLsxAenVIafGSWRE3" width="600" height="400" loading="lazy"></p>
<p>连接成功后，会在终端上显示 connected，可以到 DynamoDB 控制台中看表中是否新增了终端连接的 connection id 记录，来确保 lambda 函数已经生效。</p>
<p><img src="https://cdn-media-1.freecodecamp.org/images/uMqXnECECOiDAkC4NcS4OYjWgpvLOsUBNA8z" alt="uMqXnECECOiDAkC4NcS4OYjWgpvLOsUBNA8z" width="600" height="400" loading="lazy"></p>
<p>同理，也可以通过在按下 CTRL + C 来模拟断开连接来测试 disconnect。</p>
<p>现在已经测试了两个路由，接下来看看自定义的 <code>onMessage</code> 路由。<code>onMessage</code> 路由的功能是接受客户端发送的消息，并将该消息发送到所有连接到该 WebSocket API 的客户端。为此我们需要另一个 lambda 函数，来从 DynamoDB 表中查询所有的已连接客户端的 connection id，然后给他们推送消息。</p>
<p>首先如上创建一个名为 <code>ChatRoomOnMessageFunction</code> 的 lambda 函数，代码如下：</p>
<pre><code class="language-javascript">const AWS = require('aws-sdk');const ddb = new AWS.DynamoDB.DocumentClient();require('./patch.js');

let send = undefined;
function init(event) {
    console.log(event)
    const apigwManagementApi = new AWS.ApiGatewayManagementApi({    
      apiVersion: '2018-11-29',
      endpoint: event.requestContext.domainName + '/' + event.requestContext.stage
    });
    send = async (connectionId, data) =&gt; {
    await apigwManagementApi.postToConnection({
      ConnectionId: connectionId,
      Data: `Echo: ${data}` }).promise();  
    }
}

exports.handler =  (event, context, callback) =&gt; {
    init(event);
    let message = JSON.parse(event.body).message      
    getConnections().then((data) =&gt; {
        console.log(data.Items);          
        data.Items.forEach(function(connection) {           
            console.log("Connection " +connection.connectionid)           
            send(connection.connectionid, message);
        });
    });
  return {}
};

function getConnections(){
  return ddb.scan({
    TableName: 'Chat',
  }).promise();
}
</code></pre>
<p>上面的代码会扫描 DynamoDB 表中所有的记录。然后使用 API 中提供的 Connection URL 向其发送消息。在代码中，lambda 函数会解析 <code>message</code> 属性，然后将其发送到所有的已连接的客户端。</p>
<p>由于 WebSockets API 刚发布不久不够完善，还在迭代。需要我们手动执行一些操作。创建一个名为 <code>patch.js</code> 的文件，在其添加如下代码。</p>
<pre><code class="language-javascript">require('aws-sdk/lib/node_loader');
var AWS = require('aws-sdk/lib/core');
var Service = AWS.Service;var apiLoader = AWS.apiLoader;
apiLoader.services['apigatewaymanagementapi'] = {};
AWS.ApiGatewayManagementApi = Service.defineService('apigatewaymanagementapi', ['2018-11-29']);
Object.defineProperty(apiLoader.services['apigatewaymanagementapi'], '2018-11-29', {
    get: function get() {
        var model = {
            "metadata": {
                "apiVersion": "2018-11-29",
                "endpointPrefix": "execute-api",
                "signingName": "execute-api",
                "serviceFullName": "AmazonApiGatewayManagementApi",
                "serviceId": "ApiGatewayManagementApi",
                "protocol": "rest-json",
                "jsonVersion": "1.1",
                "uid": "apigatewaymanagementapi-2018-11-29",
                "signatureVersion": "v4"
            },
            "operations": {
                "PostToConnection": {
                    "http": {
                        "requestUri": "/@connections/{connectionId}",
                        "responseCode": 200
                    },
                    "input": {
                        "type": "structure",
                        "members": {
                            "Data": {
                                "type": "blob"
                            },
                            "ConnectionId": {
                                "location": "uri",
                                "locationName": "connectionId"
                            }
                        },
                        "required": [
                            "ConnectionId",
                            "Data"
                        ],
                        "payload": "Data"
                    }
                }      
            },
            "shapes": {}
        }
        model.paginators = {
            "pagination": {}
        }
        return model;
    },
    enumerable: true,
    configurable: true
});
module.exports = AWS.ApiGatewayManagementApi;
</code></pre>
<p>这些代码参考了<a href="https://hackernoon.com/websockets-api-gateway-9d4aca493d39">这篇文章</a>。代码的功能是为 API 自动生成 Callback URL 然后发送 POST 请求。</p>
<p><img src="https://cdn-media-1.freecodecamp.org/images/Uq12ZG3KNn38ut5jQQLRjOPebZBuLIxxqesW" alt="Uq12ZG3KNn38ut5jQQLRjOPebZBuLIxxqesW" width="600" height="400" loading="lazy"></p>
<p>现在已经创建了 lambda 函数，可以继续在 API Gateway 中创建自定义路由。在新的 Route Key 中，添加 <code>onMessage</code> 自定义路由。完成路由配置后，将 lambda 函数添加到此路由上然后部署 API。</p>
<p>现在已经完成了 WebSocket API，可以完整的测试这个应用程序了。可以打开多个终端来模拟多个客户端间发送消息。</p>
<p>连接成功后，用如下 JSON 发送消息：</p>
<pre><code>{"action" : "onMessage" , "message" : "Hello everyone"}
</code></pre>
<p>在这里，<code>action</code> 是我们自定义的路由，<code>message</code> 是需要发送到其它客户端的消息。</p>
<p><img src="https://cdn-media-1.freecodecamp.org/images/hHo2bGE-lEcSiKIF9CNUpHwXJrKj05h2F5mV" alt="hHo2bGE-lEcSiKIF9CNUpHwXJrKj05h2F5mV" width="600" height="400" loading="lazy"></p>
<p>这就是使用  AWS WebSocket API 实现的简单的聊天应用程序。实际上我们并未配置 <code>$default</code> 路由，该路由会在路由无法成功匹配时调用，这个路由的实现就交给你啦。此致，下一篇文章见 ：）</p>
<!--kg-card-end: markdown--><p>原文：<a href="https://www.freecodecamp.org/news/real-time-applications-using-websockets-with-aws-api-gateway-and-lambda-a5bb493e9452/">How to build real-time applications using WebSockets with AWS API Gateway and Lambda</a>，作者：Janitha Tennakoon</p> ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ 通过构建你自己的 Slack App 来学习 Serverless ]]>
                </title>
                <description>
                    <![CDATA[ Serverless 架构在业内炙手可热，很多大厂已经开始拥抱它了。 本文将介绍 Serverless 是什么，为什么要拥抱它。以及 AWS 设置，如何创建 serverless app，如何创建 slack app。 什么是 Serverless Serverless 是云计算范例，开发者只需关注代码，无需担忧服务器运维。 云提供商，比如 AWS 或者 Azure，通过动态分配资源来运行代码以及运维服务。很多事件都可以触发代码，包括定时任务、http 请求或者数据库事件。 开发者发送到云端的代码通常只是一个函数，因此，serverless 架构通常实现为函数即服务（Function-as-a-Service），即 FaaS。很多云服务商提供 FaaS 框架，比如 AWS 的 Lambda 和 Azure 的 Functions。 为什么使用 Serverless Serverless 除了让开发者只关心代码外，还有如下优势。 由于云服务商负责运行代码以及分配资源，所以可以按请求数或者代码执行次数付费。 另外，由于云服务商接管服务器，不必担心流量涌入等问题，云服务商会搞定 ]]>
                </description>
                <link>https://www.freecodecamp.org/chinese/news/make-a-serverless-slack-app/</link>
                <guid isPermaLink="false">5dbaf0e4ca1efa04e196a2bb</guid>
                
                    <category>
                        <![CDATA[ Slack ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Serverless ]]>
                    </category>
                
                    <category>
                        <![CDATA[ AWS ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ ZhichengChen ]]>
                </dc:creator>
                <pubDate>Fri, 01 Nov 2019 02:00:00 +0000</pubDate>
                <media:content url="https://chinese.freecodecamp.org/news/content/images/2021/04/photo-1572481566261-4e2503288d82.jpeg" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>Serverless 架构在业内炙手可热，很多大厂已经开始拥抱它了。</p><p>本文将介绍 Serverless 是什么，为什么要拥抱它。以及 AWS 设置，如何创建 serverless app，如何创建 slack app。</p><h2 id="-serverless">什么是 Serverless</h2><p>Serverless 是云计算范例，开发者只需关注代码，无需担忧服务器运维。</p><p>云提供商，比如 AWS 或者 Azure，通过动态分配资源来运行代码以及运维服务。很多事件都可以触发代码，包括定时任务、http 请求或者数据库事件。</p><p>开发者发送到云端的代码通常只是一个函数，因此，serverless 架构通常实现为函数即服务（Function-as-a-Service），即 FaaS。很多云服务商提供 FaaS 框架，比如 AWS 的 Lambda 和 Azure 的 Functions。</p><h2 id="-serverless-1">为什么使用 Serverless</h2><p>Serverless 除了让开发者只关心代码外，还有如下优势。</p><p>由于云服务商负责运行代码以及分配资源，所以可以按请求数或者代码执行次数付费。</p><p>另外，由于云服务商接管服务器，不必担心流量涌入等问题，云服务商会搞定。综上，serverless app 便宜、易维护、高可用。</p><h2 id="-aws-lambda">设置 AWS Lambda</h2><p>在这个教程里会使用 AWS Lambda，首先来创建一个 <a href="https://aws.amazon.com/">AWS 账号</a>。AWS UI 不是很友好，我会给每个步骤贴一个截图。</p><p>登录后，如下：</p><figure class="kg-card kg-image-card"><img src="https://www.freecodecamp.org/news/content/images/2019/08/image-17.png" class="kg-image" alt="image-17" width="600" height="400" loading="lazy"></figure><p>首屏</p><p>接下来，创建一个 IAM 用户。<a href="https://docs.aws.amazon.com/IAM/latest/UserGuide/id_users.html">IAM</a> &nbsp;(Identity and Access Management) 用户可以操作 AWS 以及指定的资源。可以按场景创建具有相应权限的不同的 IAM 用户，不必担忧泄露 root 用户账号从而引发安全隐患。</p><p>点击页面导航栏的 "services“ ，在搜索框输入 ”IAM“ ：</p><figure class="kg-card kg-image-card"><img src="https://www.freecodecamp.org/news/content/images/2019/08/image-27.png" class="kg-image" alt="image-27" width="600" height="400" loading="lazy"></figure><p>单击第一个搜索结果，看到如下的左侧边栏，这就是 IAM 控制台了。点击 ”User“ 选项来创建一个新的 IAM 用户。</p><figure class="kg-card kg-image-card"><img src="https://www.freecodecamp.org/news/content/images/2019/08/image-28.png" class="kg-image" alt="image-28" width="600" height="400" loading="lazy"></figure><p>点击 ”Add user“ 按钮创建一个新用户。按照下图填写详情：</p><figure class="kg-card kg-image-card"><img src="https://www.freecodecamp.org/news/content/images/2019/08/image-29.png" class="kg-image" alt="image-29" width="600" height="400" loading="lazy"></figure><p>IAM 用户用什么名字都可以，在这里用的是 <code>serverless-admin</code>。确保选中了 ”Programmatic access“，取消 ”AWS Management Console Access“ 选中。 后者是给团队成员或者其它需要访问 AWS 的人用的。这里的 IAM 用户只需要和 AWS Lambda 交互，只需要给予 programmatic 权限即可。</p><p>在权限这里，因为还没有任何 Groups，也没有存在的可以复制权限的用户，选择 Attach existing policies。由于只是一个私人的项目，这里创建了一个具有超级管理员（Administrator）访问权限的用户。如果在生产环境使用 serverless app，IMA 用户应该限制为只具有访问 AWS Lambda 的权限。（<a href="https://serverless.com/blog/abcs-of-iam-permissions/">这里</a>有介绍）。</p><figure class="kg-card kg-image-card"><img src="https://www.freecodecamp.org/news/content/images/2019/08/image-58.png" class="kg-image" alt="image-58" width="600" height="400" loading="lazy"></figure><p>我没有给创建的用户添加任何标签。这对于接下来保存 Access ID 和 Secret Access Key 信息很重要。</p><figure class="kg-card kg-image-card"><img src="https://www.freecodecamp.org/news/content/images/2019/08/Screenshot_2019-08-04-IAM-Management-Console.png" class="kg-image" alt="Screenshot_2019-08-04-IAM-Management-Console" width="600" height="400" loading="lazy"></figure><p>千万不要在没有复制和下载 key 前关掉这个页面。关掉了就在也看不到 Secret access key 了。</p><p>最后，使用 AWS 命令行添加 credentials 。可以根据这个<a href="https://docs.aws.amazon.com/cli/latest/userguide/cli-chap-install.html">指引</a>来安装 aws cli。</p><p>运行 <code>aws --version</code>来确保已经安装成功。如下：</p><figure class="kg-card kg-image-card"><img src="https://www.freecodecamp.org/news/content/images/2019/08/Screen-Shot-2019-08-04-at-2.02.27-PM.png" class="kg-image" alt="Screen-Shot-2019-08-04-at-2.02.27-PM" width="600" height="400" loading="lazy"></figure><p>接着运行 <code>aws configure</code> 填写 Key 信息：</p><figure class="kg-card kg-image-card"><img src="https://www.freecodecamp.org/news/content/images/2019/08/Screen-Shot-2019-08-04-at-5.42.53-PM.png" class="kg-image" alt="Screen-Shot-2019-08-04-at-5.42.53-PM" width="600" height="400" loading="lazy"></figure><p>上面默认的地区就是 <code>us-east-2</code>, 可以通过<a href="https://docs.aws.amazon.com/cli/latest/userguide/cli-chap-install.html">这个</a>来确定你的地区是什么。</p><p>为了确保 credentials 设置成功，可以在终端里运行 <code>cat ~/.aws/credentials</code>。</p><p>如果想设置某一个属性，可以运行如下命令：<code>aws configure --profile [profile name]</code>。</p><p>如果有和预期不符的地方，可以查询 <a href="https://docs.aws.amazon.com/cli/latest/userguide/cli-chap-configure.html">AWS 的文档</a>。</p><h2 id="-serverless-2">设置 serverless</h2><p>打开终端执行命令 <code>npm i -g serverless</code> 在全局安装 <code>serverless</code> 框架。如下所示。<a href="https://serverless.com/">戳此了解更多 serverless 框架</a>。</p><figure class="kg-card kg-image-card"><img src="https://www.freecodecamp.org/news/content/images/2019/08/Screen-Shot-2019-08-04-at-1.55.12-PM.png" class="kg-image" alt="Screen-Shot-2019-08-04-at-1.55.12-PM" width="600" height="400" loading="lazy"></figure><p>接着，到工作目录下，运行 <code>serverless</code>，按如下填写：</p><figure class="kg-card kg-image-card"><img src="https://www.freecodecamp.org/news/content/images/2019/08/Screen-Shot-2019-08-04-at-5.55.03-PM.png" class="kg-image" alt="Screen-Shot-2019-08-04-at-5.55.03-PM" width="600" height="400" loading="lazy"></figure><p>在这里使用 Node.js。可以给 app 起任何名字，在这里使用 <code>exampleSlackApp</code>。</p><p>用代码编辑器打开 <code>exampleSlackApp</code>（或者你命名的项目）。</p><p>首先，打开 <code>serverless.yml</code>。会看到有大量的注释的代码描述了可以再这个文件使用的不同的选项。可以了解一下，下面的文件它们都删除了，便于显示：</p><pre><code>service: exampleslackapp

provider:
  name: aws
  runtime: nodejs10.x
  region: us-east-2

</code></pre><p>serverless.yml</p><p>由于默认的 <code>region</code> 是 <code>us-east-1</code> 我添加了 <code>region</code> &nbsp;改成 <code>us-east-2</code>。</p><p>运行 <code>serverless deploy</code> 来部署 <code>serverless</code> 刚刚创建的 app。输出如下：</p><figure class="kg-card kg-image-card"><img src="https://www.freecodecamp.org/news/content/images/2019/08/Screen-Shot-2019-08-05-at-12.07.10-AM.png" class="kg-image" alt="Screen-Shot-2019-08-05-at-12.07.10-AM" width="600" height="400" loading="lazy"></figure><p>如果在终端运行 <code>serverless invoke -f hello</code>，会启动 app，输出如下：</p><pre><code>{
    "statusCode": 200,
    "body": "{\n  "message": "Go Serverless v1.0! Your function executed successfully!",\n  "input": {}\n}"
}
</code></pre><p>为了证明 slack app 已经在线，回到 AWS 控制台。打开 services 下拉菜单，搜索 ”Lambda“，选择第一个选项（Run code without thinking about servers）。</p><figure class="kg-card kg-image-card"><img src="https://www.freecodecamp.org/news/content/images/2019/08/image-32.png" class="kg-image" alt="image-32" width="600" height="400" loading="lazy"></figure><p>这里就是我们的 app！</p><figure class="kg-card kg-image-card"><img src="https://www.freecodecamp.org/news/content/images/2019/08/image-33.png" class="kg-image" alt="image-33" width="600" height="400" loading="lazy"></figure><p>接下来，通过 serverless 构建 slack app，可以通过 &nbsp;<a href="https://api.slack.com/slash-commands">slash command</a> 向 slack 随机发送 <a href="https://en.wikipedia.org/wiki/Ron_Swanson">Ron Swanson</a> 的名言，如下：</p><figure class="kg-card kg-image-card"><img src="https://www.freecodecamp.org/news/content/images/2019/08/Screen-Shot-2019-08-07-at-10.23.40-PM.png" class="kg-image" alt="Screen-Shot-2019-08-07-at-10.23.40-PM" width="600" height="400" loading="lazy"></figure><p>下面的步骤不必按顺序做，如果想跳着来，随意。</p><h2 id="-api">给我们的代码添加 API</h2><p>我使用<a href="https://github.com/jamesseanwright/ron-swanson-quotes#ron-swanson-quotes-api?ref=public-apis">这个 API</a> 来生成 Ron Swanson 的名言，它的文档很清晰（最重要的是免费）。如果想知道怎么调用以及返回格式，把这个链接复制到浏览器，访问一下：</p><p><a href="https://ron-swanson-quotes.herokuapp.com/v2/quotes"><code>https://ron-swanson-quotes.herokuapp.com/v2/quotes</code></a></p><p>结果如下：</p><figure class="kg-card kg-image-card"><img src="https://www.freecodecamp.org/news/content/images/2019/08/image-59.png" class="kg-image" alt="image-59" width="600" height="400" loading="lazy"></figure><p>打开默认的函数改成如下：</p><pre><code>module.exports.hello = (event) =&gt; {
  getRon();
};
</code></pre><p>注：我已经移除了 async</p><p><code>getRon</code> 方法如下</p><pre><code>function getRon() {
  request('https://ron-swanson-quotes.herokuapp.com/v2/quotes', function (err, resp, body) {
    console.log('error:', err)
    console.log('statusCode:', resp &amp;&amp; resp.statusCode)
    console.log('body', body)
  })
}
</code></pre><p>现在，检查一下是否正常。在终端里执行：<code>serverless invoke local -f hello</code>，在本地测试代码。输出如下：</p><figure class="kg-card kg-image-card"><img src="https://www.freecodecamp.org/news/content/images/2019/08/Screen-Shot-2019-08-07-at-9.41.53-PM.png" class="kg-image" alt="Screen-Shot-2019-08-07-at-9.41.53-PM" width="600" height="400" loading="lazy"></figure><p>Spoiler: There was a wrong way to consume alcohol</p><p><code>serverless invoke -f hello</code> 会运行已经部署的代码，如前文所述。</p><p><code>serverless invoke local -f hello</code>，在本地运行代码，用它来测试很方便。测试无虞后 <code>serverless deploy</code> 部署一下，棒。</p><h2 id="-slack-app">创建 Slack App</h2><p>按照这个<a href="https://api.slack.com/apps?new_app=1">链接</a>创建 slack app。需要先注册 slack worksapce，确保有添加 slack app 的权限。这里创建了一个测试。弹出这个弹窗后，信息按需填写，如下：</p><figure class="kg-card kg-image-card"><img src="https://www.freecodecamp.org/news/content/images/2019/08/image-61.png" class="kg-image" alt="image-61" width="600" height="400" loading="lazy"></figure><p>接下来，是 app 的首页。这里可以好好研究一下，我是像下面这样定制的 app：</p><figure class="kg-card kg-image-card"><img src="https://www.freecodecamp.org/news/content/images/2019/08/image-62.png" class="kg-image" alt="image-62" width="600" height="400" loading="lazy"></figure><p>Display information 可以在 app 的 ”Basic information“ 标签页打开</p><p>接下来需要给 app 添加一些权限：</p><figure class="kg-card kg-image-card"><img src="https://www.freecodecamp.org/news/content/images/2019/08/Screenshot_2019-08-07-Slack-API-Applications-lekha_test-Slack.png" class="kg-image" alt="Screenshot_2019-08-07-Slack-API-Applications-lekha_test-Slack" width="600" height="400" loading="lazy"></figure><p>向下滚动页面，添加 scope 和 permissions 来获得 OAuth Access Token。</p><figure class="kg-card kg-image-card"><img src="https://www.freecodecamp.org/news/content/images/2019/08/image-64.png" class="kg-image" alt="image-64" width="600" height="400" loading="lazy"></figure><p>I've added "Modify your public channels" so that the bot could write to a channel, "Send messages as Ron Swanson" so when the message gets posted, it looks like a user called Ron Swanson is posting the message, and slash commands so the user can "request" a quote as shown in the screenshot at the beginning of the article. After you save the changes, you should be able to scroll back up to OAuths &amp; Permissions to see:</p><p>添加 ”Modify your public channels“ 以便机器人可以向 channel 发消息。添加 ”Send messages as Ron Swaon“，这样收到信息时，看起来像是 Ron Swanson 用户发送的，以及 ”slash commands“，以便用户可以像文章开头的截图那样请求名言。点击 Save Changes 后，滚动回 OAuths &amp; Permissions 如下：</p><figure class="kg-card kg-image-card"><img src="https://www.freecodecamp.org/news/content/images/2019/08/image-65.png" class="kg-image" alt="image-65" width="600" height="400" loading="lazy"></figure><p>点击 Install App to Workspace 按钮，就会获得一个 OAuth Access Token。一会会用到它，复制或者下载它到某个地方。</p><h2 id="-slack-app-1">链接代码到 Slack App</h2><p>在 AWS Lambda 里，找到对应的 slack app 函数。函数代码部分应该显示更新过的代码，包含调用 Ron Swanson API 的代码（如果没有，在终端里执行一下 <code>serverless deploy</code>）。</p><p>滚动屏幕到 ”Environment Variables“ 部分，把 Slack OAuth Access Token 放在这里（给 key 起一个名字）：</p><figure class="kg-card kg-image-card"><img src="https://www.freecodecamp.org/news/content/images/2019/08/Screenshot_2019-08-07-Lambda-Management-Console.png" class="kg-image" alt="Screenshot_2019-08-07-Lambda-Management-Console" width="600" height="400" loading="lazy"></figure><p>返回代码在函数里添加 Slack key。在文件顶部，声明一个 <code>const</code> ，引用 OAuth Token。</p><p>const SLACK_OAUTH_TOKEN = process.env.OAUTH_TOKEN`.</p><p><code>process.env</code> 只是获取到环境变量（<a href="https://nodejs.org/dist/latest-v8.x/docs/api/process.html#process_process_env">戳此查看介绍</a>）。接下来看一下 <a href="https://api.slack.com/methods/chat.postMessage">Slack API</a> 来研究一下怎么向 channel 里发消息。</p><figure class="kg-card kg-image-card"><img src="https://www.freecodecamp.org/news/content/images/2019/08/image-67.png" class="kg-image" alt="image-67" width="600" height="400" loading="lazy"></figure><figure class="kg-card kg-image-card"><img src="https://www.freecodecamp.org/news/content/images/2019/08/image-76.png" class="kg-image" alt="image-76" width="600" height="400" loading="lazy"></figure><p>上面的两幅图片是我在 API 文档里面截的，参考文档发起对应请求，这里使用 <code>request</code> 方法传入了一个名为 <code>options</code> 的对象：</p><pre><code>  let options = {
    url: 'https://slack.com/api/chat.postMessage',
    headers: {
      'Accept': 'application/json',
    },
    method: 'POST',
    form: {
      token: SLACK_OAUTH_TOKEN,
      channel: 'general', // hard coding for now
      text: 'I am here',
    }
  }
</code></pre><p>然后发起请求：</p><pre><code>  request(options, function(err, resp, body) {
    console.log('error:', err)
    console.log('statusCode:', resp &amp;&amp; resp.statusCode)
    console.log('body', body)
  })
</code></pre><p>最后封装成函数：</p><pre><code>function postRon(quote) {
  let options = {
    url: 'https://slack.com/api/chat.postMessage',
    headers: {
      'Accept': 'application/json',
    },
    method: 'POST',
    form: {
      token: SLACK_OAUTH_TOKEN,
      channel: 'general',
      text: quote,
    }
  }

</code></pre><p>给这个函数命名为 <code>getRon</code>，如下：</p><pre><code>function getRon() {
  request('https://ron-swanson-quotes.herokuapp.com/v2/quotes', function (err, resp, body) {
    console.log('error:', err)
    console.log('statusCode:', resp &amp;&amp; resp.statusCode)
    console.log('body', body)
    postRon(body.substring(2, body.length - 2)) // here for parsing, remove if you want to see how/why I did it
  })
}
</code></pre><p>整个代码看起来如下：</p><pre><code>'use strict';
let request = require('request');
const SLACK_OAUTH_TOKEN = process.env.OAUTH_TOKEN
module.exports.hello = (event) =&gt; {
  getRon();
};
function getRon() {
  request('https://ron-swanson-quotes.herokuapp.com/v2/quotes', function (err, resp, body) {
    console.log('error:', err)
    console.log('statusCode:', resp &amp;&amp; resp.statusCode)
    console.log('body', body)
    postRon(body.substring(2, body.length - 2))
  })
}
function postRon(quote) {
  let options = {
    url: 'https://slack.com/api/chat.postMessage',
    headers: {
      'Accept': 'application/json',
    },
    method: 'POST',
    form: {
      token: SLACK_OAUTH_TOKEN,
      channel: 'general',
      text: quote,
    }
  }

</code></pre><p>现在来测试一下！很不幸 AWS Lambda 里的环境变量在运行 <code>serverless invoke local -f hello</code> 时并不可用。有很多方法来解决这个问题，这里我们把 <code>SLACK_OAUTH_TOKEN</code> 替换成真实的 OAuth Token（确保以字符串的形式）。一定要记得不要把它提交到版本控制里面去哦。</p><p>运行 <code>serverless invoke local -f hello</code>，在 #general channel 会看到类似下面的信息：</p><figure class="kg-card kg-image-card"><img src="https://www.freecodecamp.org/news/content/images/2019/08/image-69.png" class="kg-image" alt="image-69" width="600" height="400" loading="lazy"></figure><p><em>注意我的是测试 workspace 所以 channel 名字是 <code>general</code>，如果在真实的 workspace 里，应该为测试 app 创建一个单独的 channel，在测试的时候把消息发到这里面</em>。</p><p>终端里输出如下：</p><figure class="kg-card kg-image-card"><img src="https://www.freecodecamp.org/news/content/images/2019/08/Screen-Shot-2019-08-07-at-10.48.38-PM.png" class="kg-image" alt="Screen-Shot-2019-08-07-at-10.48.38-PM" width="600" height="400" loading="lazy"></figure><p>如果正常，使用命令 <code>serverless deploy</code> 部署。如果有问题，通过 <code>serverless invoke local -f hello</code> 来debug。</p><h2 id="-slash-command">添加 slash command</h2><p>最后一件事就是添加 slash command！返回 AWS Lambda 函数的首页，找到 “Add trigger” 按钮：</p><figure class="kg-card kg-image-card"><img src="https://www.freecodecamp.org/news/content/images/2019/08/image-70.png" class="kg-image" alt="image-70" width="600" height="400" loading="lazy"></figure><p>我们将要添加 API Gateway（其实已经存在了）。</p><p>单击按钮打开 “Add trigger” 页，在列表中选择 "API Gateway"：</p><figure class="kg-card kg-image-card"><img src="https://www.freecodecamp.org/news/content/images/2019/08/image-71.png" class="kg-image" alt="image-71" width="600" height="400" loading="lazy"></figure><p>按下图填写信息：</p><figure class="kg-card kg-image-card"><img src="https://www.freecodecamp.org/news/content/images/2019/08/image-72.png" class="kg-image" alt="image-72" width="600" height="400" loading="lazy"></figure><p>这个 API 会开放使用，如果用于生产环境，需要和团队讨论一下协议标准。”Add“ API，会收到一个 API endpoint。记住它，在下一步会用到。</p><p>返回 slack app 添加 slash 命令：</p><figure class="kg-card kg-image-card"><img src="https://www.freecodecamp.org/news/content/images/2019/08/image-73.png" class="kg-image" alt="image-73" width="600" height="400" loading="lazy"></figure><p>点击 ”Create New Command“，会弹出一个新窗口来创建命令。下面是我的填写：</p><figure class="kg-card kg-image-card"><img src="https://www.freecodecamp.org/news/content/images/2019/08/Screenshot_2019-08-07-Slack-API-Applications-lekha_test-Slack-1-.png" class="kg-image" alt="Screenshot_2019-08-07-Slack-API-Applications-lekha_test-Slack-1-" width="600" height="400" loading="lazy"></figure><p>”command“ 和 ”short description“ 可以随意填写，但是 ”request URL“，需要填写刚才的 API endpoint。</p><p>最后，返回代码做最后的调整。如果尝试使用 slash command，会返回错误 - 这是因为 slack 预期收到一个 response，AWS 也预期 endpoint 触达时返回一个响应。修改代码返回 <code>callback</code>（<a href="https://docs.aws.amazon.com/lambda/latest/dg/nodejs-prog-model-handler.html">文档</a>）</p><pre><code>module.exports.hello = (event,context,callback) =&gt; {
  getRon(callback);
};
</code></pre><p>修改 <code>getRon</code> 返回 callback：</p><pre><code>function getRon(callback) {
  request('https://ron-swanson-quotes.herokuapp.com/v2/quotes', function (err, resp, body) {
    console.log('error:', err)
    console.log('statusCode:', resp &amp;&amp; resp.statusCode)
    console.log('body', body)
    callback(null, SUCCESS_RESPONSE)
    postRon(body.substring(2, body.length - 2))
  })
}
</code></pre><p>在文件顶部添加 <code>SUCCESS_RESPONSE</code>:</p><pre><code>const SUCCESS_RESPONSE = {
  statusCode: 200,
  body: null
}
</code></pre><p>可以把 callback 放在 &nbsp;<code>postRon</code> 里 - 具体取决于代码。</p><p>我的代码如下：</p><pre><code>'use strict';
let request = require('request');
const SLACK_OAUTH_TOKEN = OAUTH_TOKEN
const SUCCESS_RESPONSE = {
  statusCode: 200,
  body: null
}
module.exports.hello = (event,context,callback) =&gt; {
  getRon(callback);
};
function getRon(callback) {
  request('https://ron-swanson-quotes.herokuapp.com/v2/quotes', function (err, resp, body) {
    console.log('error:', err)
    console.log('statusCode:', resp &amp;&amp; resp.statusCode)
    console.log('body', body)
    callback(null, SUCCESS_RESPONSE)
    postRon(body.substring(2, body.length - 2))
  })
}
function postRon(quote) {
  let options = {
    url: 'https://slack.com/api/chat.postMessage',
    headers: {
      'Accept': 'application/json',
    },
    method: 'POST',
    form: {
      token: SLACK_OAUTH_TOKEN,
      channel: 'general',
      text: quote,
    }
  }

</code></pre><p>现在可以再 slack 里面使用 <code>/ron</code> 命令获取 Ron Swanson 的名言啦。如果有问题，可以使用 Cloudwatch 日志来看一下哪里出了问题：</p><figure class="kg-card kg-image-card"><img src="https://www.freecodecamp.org/news/content/images/2019/08/Screenshot_2019-08-07-Lambda-Management-Console-1-.png" class="kg-image" alt="Screenshot_2019-08-07-Lambda-Management-Console-1-" width="600" height="400" loading="lazy"></figure><p>代码现在可以运行，我们已经 hardcoded 了 channel name。但是实际上我们想在任何发送 <code>/ron</code> 的 channel 返回名言。</p><p>在函数里面使用 event。</p><pre><code>module.exports.hello = (event,context,callback) =&gt; {
  console.log(event)
  getRon(callback);
};
</code></pre><p>使用 <code>/ron</code> 来运行函数，然后检查 Cloudwatch 日志来看控制台输出了什么（可能会需要刷新）。检查最新的日志会看到如下：</p><figure class="kg-card kg-image-card"><img src="https://www.freecodecamp.org/news/content/images/2019/08/image-74.png" class="kg-image" alt="image-74" width="600" height="400" loading="lazy"></figure><p>列表的第一项（也就是 "resource"、"path"，等）是 event，如果展开它，你会发现一长列表东西，我们关注的是在底部的 “body”。</p><figure class="kg-card kg-image-card"><img src="https://www.freecodecamp.org/news/content/images/2019/08/image-75.png" class="kg-image" alt="image-75" width="600" height="400" loading="lazy"></figure><p>waldo 在哪里: 发现 param 版本</p><p>Body 是含有一些相关信息的字符串，其中的一个是 “channel_id”。可以使用 channel_id （或者 channel_name）做为参数传入创建 Slack 信息的函数。为了方便，直接给出获取该参数方法：<code>event.body.slit("&amp;")[3].split("=")[1]</code>，这会返回 channel_id。hardcoded 了一下，第三个值就是 channel_id。</p><p>Now, we can alter our code to save that string as a variable:</p><p>现在，可以获取到的字符串保存为一个变量：</p><p><code>let channel = 'general'</code>（做为 fallback）</p><pre><code>module.exports.hello = (event,context,callback) =&gt; {
  console.log(event)
  channel = event.body.split("&amp;")[3].split("=")[1]
  console.log(context)
  getGoat(callback);
};
</code></pre><p>以及 <code>postRon</code>:</p><pre><code>  let options = {
    url: 'https://slack.com/api/chat.postMessage',
    headers: {
      'Accept': 'application/json',
    },
    method: 'POST',
    form: {
      token: SLACK_OAUTH_TOKEN,
      channel: channel,
      text: quote,
    }
  }
</code></pre><p>可选的 psotRon 里的 var</p><p>最后，如果在 workspace 的任何 channel 使用 slack command，会看到 Ron Swanson 的名言。如果没有，就像之前说的那样，最好的 debug serverless app 的工具是 <code>serverless invoke local -f &lt;function name&gt;</code> 以及 Cloudwatch 日志。</p><p>希望你能成功的创建 Slack 应用！我已经在文末附了相关资料和背景阅读。希望能够解决你的问题。</p><p><em>Final Repo with code:</em> <a href="https://github.com/lsurasani/ron-swanson-slack-app/">https://github.com/lsurasani/ron-swanson-slack-app/</a></p><p>原文：<a href="https://www.freecodecamp.org/news/make-a-serverless-slack-app/">https://www.freecodecamp.org/news/make-a-serverless-slack-app/</a>，作者：Lekha Surasani</p> ]]>
                </content:encoded>
            </item>
        
    </channel>
</rss>
