第 7 章 开发 ASP.NET Core Web 应用
ASP.NET Core 基础
在本章,我们将从一个命令行应用开始,并且在不借助任何模板,脚手架和向导的情况下,最终得到一个功能完整的 Web 应用
GitHub链接:https://github.com/microservices-aspnetcore/hello-world
运行 dotnet new console 命令之后,我们首先得到一个 Program.cs 文件,修改该文件并添加配置支持
using System;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Builder;
using Microsoft.Extensions.Configuration;namespace StatlerWaldorfCorp.HelloWorld
{public class Program{public static void Main(string[] args){var config = new ConfigurationBuilder().AddCommandLine(args).Build();var host = new WebHostBuilder().UseKestrel().UseStartup<Startup>().UseConfiguration(config).Build();host.Run();}}
}
之后添加一个 Startup 类,用于配置默认的中间件,它对所有 HTTP 请求都返回 "Hello World" 响应
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.Logging;
using Microsoft.AspNetCore.Http;namespace StatlerWaldorfCorp.HelloWorld {public class Startup{public Startup(IHostingEnvironment env){}public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory){app.Run(async (context) =>{await context.Response.WriteAsync("Hello, world!\n");});}}
}
添加 NuGet 包作为项目的依赖,并直接在项目文件开头处声明要使用的 Web SDK
<Project Sdk="Microsoft.NET.Sdk" ToolsVersion="15.0"><PropertyGroup><OutputType>Exe</OutputType><TargetFramework>netcoreapp1.1</TargetFramework></PropertyGroup><ItemGroup> <PackageReference Include="Microsoft.AspNetCore.Mvc" Version="1.1.1" /><PackageReference Include="Microsoft.AspNetCore.Server.Kestrel" Version="1.1.1"/><PackageReference Include="Microsoft.Extensions.Logging" Version="1.1.1"/><PackageReference Include="Microsoft.Extensions.Logging.Console" Version="1.1.1"/><PackageReference Include="Microsoft.Extensions.Logging.Debug" Version="1.1.1"/><PackageReference Include="Microsoft.Extensions.Configuration.CommandLine" Version="1.1.1"/></ItemGroup></Project>
添加 ASP.NET MVC 中间件
GitHub链接:https://github.com/microservices-aspnetcore/webapp
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Configuration;namespace StatlerWaldorfCorp.WebApp
{public class Startup{public Startup(IHostingEnvironment env){}public IConfiguration Configuration { get; set; }public void ConfigureServices(IServiceCollection services) {services.AddMvc();}public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory){app.UseMvc(routes =>{routes.MapRoute("default",template: "{controller=Home}/{action=Index}/{id?}");});}}
}
为了让它生效,我们还需要添加 NuGet 包依赖:Microsoft.AspNetCore.Mvc
添加控制器
控制器专门负责:
(1)接收来自 HTTP 请求的输入
(2)将输入转交给与 HTTP 通信、JSON解析无关的服务类处理
(3)返回合适的响应代码及正文
using Microsoft.AspNetCore.Mvc;
namespace StatlerWaldorfCorp.WebApp.Controllers
{public class HomeController : Controller{public IActionResult Index(){return "Hello World";}}
}
只要向文件中加入上面的内容,此前创建的路由就能自动检测到这个控制器并让它生效
添加模型
我们创建了一个用于表示股票报价的简单模型
namespace StatlerWaldorfCorp.WebApp.Models
{public class StockQuote{public string Symbol { get; set; }public int Price { get; set; }}
}
添加视图
<html>
<head><title>Hello world</title>
</head>
<body><h1>Hello World</h1><div><h2>Stock Quote</h2><div>Symbol: @Model.Symbol<br/>Price: $@Model.Price<br/></div></div>
</body>
</html>
现在,我们可用修改 HomeController,不再返回示例文本,而是呈现视图
using Microsoft.AspNetCore.Mvc;
using System.Threading.Tasks;
using StatlerWaldorfCorp.WebApp.Models;namespace StatlerWaldorfCorp.WebApp.Controllers
{public class HomeController : Controller{public IActionResult Index(){var model = new StockQuote { Symbol = "HLLO", Price = 3200 };return View(model);}}
}
如果现在运行应用,很可能会收到 HTTP 500 响应
由于我们开发的是 Web 应用,因而一定希望能查看所有发生错误的堆栈信息
可用向 Startup 类的 Configure 方法中加入一行调用 UseDeveloperExceptionPage 的代码,实现这一需求
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Configuration;namespace StatlerWaldorfCorp.WebApp
{public class Startup{public Startup(IHostingEnvironment env){var builder = new ConfigurationBuilder().SetBasePath(env.ContentRootPath).AddEnvironmentVariables();Configuration = builder.Build();}public IConfiguration Configuration { get; set; }public void ConfigureServices(IServiceCollection services) {services.AddMvc();}public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory){loggerFactory.AddConsole();loggerFactory.AddDebug();app.UseDeveloperExceptionPage();app.UseMvc(routes =>{routes.MapRoute("default",template: "{controller=Home}/{action=Index}/{id?}");});app.UseStaticFiles();}}
}
有了新的 Startup 类,我们应该能够通过 dotnet restore 以及 dotnet run 启动应用
从 JavaScript 中调用 REST API
首先,我们通过添加新的控制器来创建 API 端点
using Microsoft.AspNetCore.Mvc;
using StatlerWaldorfCorp.WebApp.Models;namespace StatlerWaldorfCorp.WebApp.Controllers
{[Route("api/test")]public class ApiController : Controller{[HttpGet]public IActionResult GetTest(){return this.Ok(new StockQuote { Symbol = "API", Price = 9999 });}}
}
如果现在再运行应用,可用打开浏览器并访问 http://localhost:5000/api/test,应该能看到一个 JSON 响应
{"symbol" : "API","price" : 9999
}
有了可供消费的 API 后,现在来修改我们唯一的视图,让它调用 JavaScript 来消费这个 API
<html>
<head><title>Hello world</title><script src="https://ajax.googleapis.com/ajax/libs/jquery/1.10.2/jquery.min.js"></script><script src="/Scripts/hello.js"></script>
</head>
<body><h1>Hello World</h1><div><h2>Stock Quote</h2><div>Symbol: @Model.Symbol<br/>Price: $@Model.Price<br/></div></div><br/><div><p class="quote-symbol">The Symbol is </p><p class="quote-price">The price is $</p></div>
</body>
</html>
注意,这里决定引入一个 jQuery,以及一个新脚本 hello.js
我们按照约定,把它添加到名为 wwwroot 的新目录 wwwroot/Scripts/hello.js
$(document).ready(function () {$.ajax({url: "/api/test"}).then(function (data) {$('.quote-symbol').append(data.symbol);$('.quote-price').append(data.price);});
});
这些 jQuery 代码非常直观,它们向 API 端点发送 Ajax 请求,返回的对象会包含 symbol 和 price 属性,它们将被附加到新添加的段落标签之中
开发云原生 Web 应用
(1)API 优先
(2)配置
(3)日志
(4)会话状态
(5)数据保护
(6)后端服务
(7)环境均等
(8)端口绑定
(9)遥测
(10)身份验证和授权
会话状态
云原生 Web 应用基本上不可能再使用基于内存的会话状态了,而必须使用进程外的提供程序
数据保户
如果涉及数据保护,”进程外存储“的思路同样适用于密钥存储
我们要使用一种现成的密钥保管库,可以是基于云的密钥保管库,也可以是基于 Redis 或其他数据库制作的定制解决方案
端口绑定
不管是使用 docker compose,部署到 Kubernetes,还是使用 AWS、Azure 或者 GCP,应用要想在云环境中运行良好,就要能接受为它预设的任何端口号