使用 Angular、NestJS 和 Azure 构建您的第一个无服务器应用程序
由 Mux 主办的 DEV 全球展示挑战赛:展示你的项目!
有没有想过挑战一下,看看从零开始构建一个完整的无服务器应用程序是什么感觉?那就让我们开始吧!
本文将介绍使用Nitro技术栈引导、构建和部署完整应用程序的所有步骤:前端使用Angular ,后端使用NestJS ,部署使用Azure Serverless 平台。
我们将开发一款名为“随机猫咪小知识”的应用程序,它会从我们的 API 获取一条有趣的猫咪小知识,并将其显示在网页上,如下所示:
本文是#ServerlessSeptember活动的一部分。在这个涵盖所有 Serverless 相关内容的合集中,您可以找到其他有用的文章、详细的教程和视频。整个九月,社区成员和云技术倡导者每天都会发布新文章——没错,每天都有!
请访问https://docs.microsoft.com/azure/azure-functions/了解更多关于 Microsoft Azure 如何启用您的无服务器函数的信息。
总结要点
- 如果你喜欢 Angular,那么你应该了解一下 NestJS:它利用了相同的概念、特性和架构,但用于 Node.js 后端开发。
- 使用 Azure Functions 为 NestJS 应用准备无服务器部署非常简单
nest add @nestjs/azure-func-http,无需对现有结构进行任何更改。 - Nitro 技术栈(= Angular + NestJS + Azure Serverless)使您能够以一致、稳健且经济高效的方式构建全栈 TypeScript 应用。
这是GitHub上的最终项目源代码。
你在这里能学到什么?
本文将探讨以下内容:
- 使用 NestJS 从零开始构建无服务器 Node.js API
- 构建一个 Angular 应用并将其连接到后端,以便进行本地和远程开发。
- 使用 Azure Functions 以经济高效且可扩展的方式部署 API。
- 使用 Azure 存储静态网站托管来部署您的 Angular 应用
ng deploy
我们使用的所有物品的参考链接
- 前端使用Angular和@azure/ng-deploy
- NestJS和@nestjs/azure-func-http作为后端
- 用于 API 部署的Azure Functions
- 使用Azure 存储托管网站
- 使用Azure CLI管理资源
- Azure Functions 核心工具,用于测试和部署后端
要求
- 一个可运行的Node.js环境
- Azure CLI用于在 Azure 上创建资源。如果您不想在本地安装它,可以使用https://shell.azure.com。
您还需要一个 Azure 帐户来创建资源和部署应用程序。如果您还没有帐户,可以使用此链接免费创建一个(包含免费额度,足以满足本文的使用需求)。
入门
让我们创建一个新文件夹,将前端和后端代码都放在其中。
$ mkdir catfacts
$ cd catfacts
我们将首先构建 API 来获取随机的猫咪趣闻,然后构建前端来使用它并在网页上显示这些趣闻。
构建后端
我们的应用程序后端将使用NestJS构建。
如果您还不熟悉 NestJS,它是一个TypeScript Node.js 框架,看起来很像 Angular,可以帮助您构建企业级高效且可扩展的 Node.js 应用程序。
安装 NestJS CLI 并搭建新的服务器应用程序
使用以下命令安装 NestJS CLI 并创建一个新的服务器应用程序:
$ npm install -g @nestjs/cli
$ nest new catfacts-server
$ cd catfacts-server
创建随机事实端点
然后我们再次使用 NestJS CLI 创建一个新的控制器:
$ nest generate controller facts
打开文件src/facts/facts.controller.ts,在导入语句之后添加以下猫咪资料列表:
// Some cat facts, courtesy of https://catfact.ninja
const catFacts = [
"Cats have supersonic hearing",
"On average, cats spend 2/3 of every day sleeping. That means a nine-year-old cat has been awake for only three years of its life.",
"A cat uses its whiskers for measuring distances. The whiskers of a cat are capable of registering very small changes in air pressure.",
"A healthy cat has a temperature between 38 and 39 degrees Celcius.",
"A cat’s jaw can’t move sideways, so a cat can’t chew large chunks of food.","Jaguars are the only big cats that don't roar.",
"Cats have 'nine lives' thanks to a flexible spine and powerful leg and back muscles",
"The cat's tail is used to maintain balance.",
"The technical term for a cat’s hairball is a 'bezoar.'",
"The first cat show was organized in 1871 in London. Cat shows later became a worldwide craze.",
"A happy cat holds her tail high and steady.",
"A cat can jump 5 times as high as it is tall."
];
然后,在现有FactsController类中添加一个新方法,如下所示:
@Controller('facts')
export class FactsController {
@Get('random')
getRandomFact(): string {
return catFacts[Math.floor(Math.random() * catFacts.length)];
}
}
现在让我们来详细分析一下我们刚才做了什么:
-
该
@Controller()注解指定此类将处理传入的请求并向客户端返回响应'facts'。此处使用的可选参数将用作该类中定义的所有处理程序的基本路由前缀。 -
该
@Get()注解定义了一个新的 HTTP GET 请求处理程序,并创建了一个新的端点。可选参数'random'将用作此端点的路径。
NestJS 将控制器路径前缀与请求处理程序路径结合起来,创建GET /facts/randomHTTP 端点。
此端点将返回状态码200和相关的响应,在本例中,响应只是一个字符串。
您可以查看 NestJS控制器文档,了解可用于定义端点的所有注解和选项的列表。
添加全局 API 前缀
一个常见的良好做法是为所有端点定义一个全局路径前缀,这样可以方便地对 API 进行版本控制,或者将其与静态资源一起公开。
在 NestJS 中,您可以通过编辑配置文件并在应用创建后src/main.ts调用该方法来实现这一点:setGlobalPrefix()
const app = await NestFactory.create(AppModule);
app.setGlobalPrefix('api');
更改后,我们的 HTTP 端点将是GET /api/facts/random。
运行服务器
我们的服务器现已准备就绪,可用于本地测试,您可以使用以下命令运行它:
$ npm start
服务器启动后,您可以使用以下命令测试我们的 API 是否响应正常curl:
$ curl http://localhost:3000/api/facts/random
每次运行此命令时,您都应该在控制台中看到一条随机的猫咪小知识。现在您的 API 已经可以正常工作了!🎉
让我们保持服务器运行,打开一个新的终端,然后继续进行前端开发。
构建前端
是时候开发我们的网页应用来展示猫咪知识啦!🐈
我们将使用Angular构建应用程序的前端,并探索一些 CLI 选项,以便更轻松地与在本地运行的服务器 API 进行开发操作。
安装 Angular CLI 并搭建新的客户端应用程序
让我们安装 Angular CLI,并使用以下命令创建客户端应用程序:
# Make sure to go back to your project root first!
$ npm install @angular/cli
$ ng new catfacts-client --defaults
$ cd catfacts-client
准备您的应用程序配置
首先,让我们apiUrl在环境配置中添加一个新属性src/environments/environment.ts,以便我们可以指定在开发环境中使用 API 的位置(一旦服务器部署完毕,我们将处理生产环境 URL):
export const environment = {
production: false,
apiUrl: '/api'
};
仅使用相对路径/api意味着我们将在与客户端相同的域上进行 API 调用,因此我们必须为 Angular CLI 开发服务器设置代理,以便将这些调用转发到本地运行的 API 服务器。
为此,我们将proxy.local.js在客户端项目的根目录下创建一个新文件:
/*
* This allows you to proxy HTTP requests like `http.get('/api/stuff')` to another server/port.
* This is especially useful during development to avoid CORS issues while using a local server.
* For more details and options see: https://angular.io/guide/build#proxying-to-a-backend-server
*/
module.exports = {
'/api': {
target: 'http://localhost:3000',
secure: false
}
};
然后,我们将在文件中添加一个新的 NPM 脚本package.json来使用它:
"scripts": {
"start:local": "ng serve --proxy-config proxy.local.js",
...
}
现在我们可以运行该命令npm run start:local,Angular CLI 将启动一个带有代理的开发服务器来使用我们正在运行的本地 NestJS 后端的 API。
获取一条随机信息并显示出来
为了从我们的 API 获取数据,我们将使用 Angular 内置的模块HttpClient,因此我们首先需要导入该模块src/app/app.module.ts:
import { HttpClientModule } from '@angular/common/http';
然后将该模块添加到您的AppModule导入声明中:
imports: [
BrowserModule,
HttpClientModule
],
之后,打开src/app/app.component.ts并修改它以进行 API 调用:
import { Component } from '@angular/core';
import { HttpClient } from '@angular/common/http'
import { Observable } from 'rxjs';
import { environment } from '../environments/environment';
@Component({
selector: 'app-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.css']
})
export class AppComponent {
fact$: Observable<string>;
constructor(http: HttpClient) {
this.fact$ = http.get(
`${environment.apiUrl}/facts/random`,
{ responseType: 'text' }
);
}
}
关键在于这段代码:
this.fact$ = http.get(
`${environment.apiUrl}/facts/random`,
{ responseType: 'text' }
);
这里我们使用构造函数中注入的 Angular HTTP 客户端,通过apiUrl环境配置中的前缀向我们的猫咪资料 API 发送 GET HTTP 请求。由于请求结果只是纯文本而不是 JSON 对象,我们也显式地将响应类型设置为 `null`,'text'这样 Angular 就不会尝试解析它。
请注意,这行代码会创建可观察对象,但只有在我们订阅该可观察对象
fact$后才会发出实际的 HTTP 请求! 为此,我们将在模板中使用 `subscribe` 函数,该函数会自动订阅/取消订阅此可观察对象。AsyncPipe
现在请编辑src/app/app.component.html并替换所有模板化的HTML代码,以显示我们的猫咪小知识:
<article>
<p>{{ fact$ | async }}</p>
<p>😸</p>
</article>
我们async在这里使用管道来完成所有神奇的操作:它会订阅fact$可观察对象,等待接收到数据后再显示它,并在组件销毁时通过取消订阅来处理清理工作。
在本地测试结果
npm run start:local请确保您的 API 服务器仍在运行,如果尚未运行,请使用命令运行前端开发服务器。
在浏览器中打开该网址http://localhost:4200,您应该会看到类似这样的内容:
您的应用(本地)运行正常!
(可选)喷漆
让我们添加一些 CSS 代码,让你的应用更美观!🦄
可以src/styles.css添加微 CSS 重置和背景渐变:
html, body {
margin: 0;
height: 100%;
background: linear-gradient(120deg, #f6d365, #fda085);
}
现在我们可以src/app/app.component.css自定义猫咪小知识的样式了,比如更改字体和添加一些边距:
@import url('https://fonts.googleapis.com/css?family=Caveat&display=swap');
p {
font-family: 'Caveat', cursive;
font-size: 2.5rem;
margin: 2rem;
}
提示:您可以访问https://fonts.google.com,选择您最喜欢的字体!
我们还要将文章块垂直居中,文本水平居中:
article {
text-align: center;
position: absolute;
top: 50%;
transform: translateY(-50%);
width: 100%;
}
现在结果应该好一些了,像这样:
部署您的应用
至此,您的应用程序应该已经在本地运行,现在是时候将其部署到云端了。
Go Serverless
为什么选择无服务器架构?为什么不将服务器部署在容器或Azure 应用服务上呢?主要有两个原因:
- 您只需为使用的资源付费,无需为资源分配付费(而且价格非常便宜!)
- 它可自动扩展,无需任何设置。
首先,让我们将 NestJS 更新为无服务器应用程序,以便我们可以将其部署到Azure Functions。
打开终端并切换到服务器文件夹,然后运行以下命令:
$ nest add @nestjs/azure-func-http
就这样……真的。
借助提供的原理图,您的服务器代码现在可以部署到 Azure Functions 了!最棒的是,您的代码没有任何更改,它仍然可以像以前一样在本地运行。
仔细观察,你会发现新增了以下内容:
main包含 Azure Functions 触发器配置和入口点的文件夹src/main.azure.ts:服务器应用程序的替代入口点,该入口点仅在 Azure Functions 上使用(因此保留常规入口点不变)。- 项目根目录下有一些配置文件,我们暂时不需要关心它们。
- 文件中新增了一个
start:azureNPM 脚本package.json,允许你在本地运行 API,但这次使用的是 Azure Functions 模拟器。
要使用该命令npm run start:azure,您必须安装Azure Functions Core Tools。它将安装funcCLI,您可以使用该 CLI 测试函数并将其部署到 Azure。
CLI安装完成后func,让我们测试一下API在函数上的运行情况:
$ npm run start:azure
如果一切正常,您应该会在控制台中看到以下日志:
Now listening on: http://0.0.0.0:7071
Application started. Press Ctrl+C to shut down.
Http Functions:
main: http://localhost:7071/api/{*segments}
这意味着您的 API 应该在端口上正常工作7071,让我们使用以下命令再次测试curl:
$ curl http://localhost:7071/api/facts/random
创建资源并部署到 Azure Functions
现在我们的 API 已经可以在 Functions 上运行了,让我们把它部署到实际环境中吧!
首先,我们需要创建一些 Azure 资源。为此,我们将使用Azure CLI命令:
# Create a new resource group
$ az group create --name catfacts --location northeurope
# Create the storage account
# This name must be globally unique, so change it with your own
$ az storage account create --name catfacts \
--resource-group catfacts \
--kind StorageV2
# Create the function app
# This name must be globally unique, so change it with your own
$ az functionapp create --name catfacts-api \
--resource-group catfacts \
--consumption-plan-location northeurope \
--storage-account catfacts
现在我们已经在 Azure 上创建了资源,可以使用funcCLI 来部署我们的 API:
# Build your app
$ npm run build
# Clean up node_modules to keep only production dependencies
$ npm prune --production
# Create an archive from your local files and publish it
# Don't forget to change the name with the one you used previously
$ func azure functionapp publish catfacts-api
发布后,您应该会在控制台中看到可用于调用该函数的 URL,如下所示:
Functions in catfacts-api:
main - [httpTrigger]
Invoke url: https://catfacts-api.azurewebsites.net/api/{*segments}
我们可以curl再次调用我们可靠的命令,使用之前的 URL 来检查部署情况:
curl https://catfacts-api.azurewebsites.net/api/facts/random
服务器部署完成。✔️
准备前端生产环境
现在服务器已经部署完毕,我们需要apiUrl在生产环境配置中添加该属性src/environments/environment.prod.ts,就像之前在开发环境中所做的那样:
export const environment = {
production: true,
// Use the function app URL you got from the previous step
apiUrl: 'https://catfacts-api.azurewebsites.net/api'
};
部署前端
最后一步是将我们的 Angular 应用部署到 Azure。为此,我们将使用 Angular CLI 和以下@azure/ng-deploy软件包:
# Make sure to use the storage account name created previously
$ ng add @azure/ng-deploy --resourceGroup catfacts --account catfacts
系统会提示您登录 Azure 帐户,然后您需要选择之前创建资源时使用的相同订阅。
由于我们希望重用后端使用的同一个存储帐户,因此必须使用以下命令在该帐户上启用静态网站托管:
# Don't forget to change the account name with the one you used previously
$ az storage blob service-properties update \
--account-name catfacts \
--static-website \
--404-document index.html \
--index-document index.html
之后,您可以使用以下命令直接从 Angular CLI 部署您的应用程序:
$ ng deploy
您的应用将构建为生产环境版本,然后部署到您的 Azure 存储网站容器中。
部署完成后,打开工具返回的 URL,其格式类似于:https://catfacts.z16.web.core.windows.net/,瞧!
等等,好像不对劲!猫咪小知识都没显示?😱
如果打开控制台,应该会看到类似这样的错误信息:
修复 CORS 问题
出现此错误的原因是,为了提高安全性,浏览器会阻止脚本向与当前网页域名不同的网站域名发出 HTTP 请求。
要绕过此限制,您的 Web 服务器必须定义特定的 HTTP 标头以允许此操作。这种机制称为跨域资源共享(CORS)。
Azure Functions 默认已启用 CORS,但您必须使用以下命令将您的网站域名添加到允许的来源列表中:
# Don't forget to change the name and URL with your own
$ az functionapp cors add \
--name catfacts-api \
--resource-group catfacts \
--allowed-origins https://catfacts.z16.web.core.windows.net
如果您刷新网页,现在应该可以正常看到关于猫的知识点了。
结束
恭喜!您已成功部署您的第一个完整的无服务器应用程序!⚡️💪
让我们快速回顾一下刚才的操作:
- 我们搭建了一个新的 NestJS 服务器,创建了 API,并为无服务器部署做好了准备。
- 我们创建了一个新的 Angular Web 应用程序来使用您的 API 并显示数据,并为开发和生产环境配置了不同的环境。
- 我们配置了 Azure 资源,并将我们的应用程序部署在了这些资源上。
虽然有点冗长,但现在您已经拥有了从启动到发布全栈无服务器应用程序的完整端到端体验🚀。
我们刚刚构建的应用程序的完整源代码可以在GithHub上找到。
欢迎在评论区分享您的反馈和经验!
更进一步
我们在这里只是浅尝辄止,但你已经看到了使用 Angular、NestJS 和 Azure 创建和部署完整应用程序是多么快速和容易。
我们目前的应用程序功能还很基础,但通过添加数据库的 CRUD 操作、文件上传等功能来扩展它并不难。
如果您想深入了解,以下是一些文章推荐:
如需查看包含文件上传和使用 Azure 存储进行数据库连接的更完整的示例应用程序,您可以查看Nitro Cats 演示源代码。
欢迎在推特上关注我,我很乐意与大家交流并接受大家的建议!
文章来源:https://dev.to/azure/build-your-first-serverless-app-with-angular-nestjs-and-azure-108h




