3、ASP.NET Core基础:创建最小API项目、理解Program.cs和启动配置、中间件管道概念
3.1 从零开始:创建最小API项目
说实话,我刚接触ASP.NET Core那会儿,也被各种模板搞晕过。MVC、Web API、Razor Pages……选哪个?
但现在不一样了。.NET 6之后,微软推出了「最小API」。说白了,就是让你用最少的代码,跑起一个Web服务。
我个人习惯用命令行创建项目,干净利落:
dotnet new web -n MyMinimalApi
cd MyMinimalApi
dotnet run
就这么三行,一个Web服务就跑起来了。你打开浏览器,访问 http://localhost:5000,就能看到 "Hello World!"。
嗯,这里要注意。这个模板生成的项目,只有一个文件——Program.cs。所有东西都塞在里面。我刚开始也觉得别扭,但用久了发现,对于小服务、微服务来说,这种方式反而清爽。
3.2 解剖Program.cs:启动配置的秘密
打开Program.cs,你会看到这样的代码:
var builder = WebApplication.CreateBuilder(args);
var app = builder.Build();
app.MapGet("/", () => "Hello World!");
app.Run();
这四行代码,背后藏着整个ASP.NET Core的启动机制。我拆开来讲。
3.2.1 builder:服务的容器
WebApplication.CreateBuilder(args) 创建了一个构建器。这个构建器,说白了就是你的「服务容器」。
你在里面注册什么服务,你的应用就能用什么服务。比如:
var builder = WebApplication.CreateBuilder(args);
// 注册数据库上下文
builder.Services.AddDbContext<AppDbContext>(options =>
options.UseSqlServer(builder.Configuration.GetConnectionString("Default")));
// 注册自定义服务
builder.Services.AddScoped<IUserService, UserService>();
我曾经在一个项目中,忘了注册服务就直接在API里注入,结果运行时直接报错。嗯,那场面挺尴尬的。所以我的习惯是:先注册,后使用。
3.2.2 app:请求管道的起点
var app = builder.Build(); 这行代码,构建了应用实例。这个app对象,就是整个请求管道的起点。
你想想看,一个HTTP请求进来,经过一系列处理,最后返回响应。这一系列处理步骤,就是「中间件管道」。
3.3 中间件管道:请求的流水线
中间件管道,你可以把它想象成一条流水线。每个中间件就是一个工位,负责处理请求的某个方面。
比如:
- 第一个工位:异常处理(捕获未处理的异常)
- 第二个工位:身份验证(检查用户是否登录)
- 第三个工位:静态文件(返回CSS、JS等静态资源)
- 第四个工位:路由(匹配URL,找到对应的处理逻辑)
每个中间件都可以决定:是继续往下传,还是直接返回响应。
3.3.1 中间件的注册顺序
这个顺序很重要。我踩过坑。
比如,你把异常处理中间件放在最后,那前面的中间件抛异常,就没人管了。请求直接挂掉,返回500错误,连个友好的提示都没有。
正确的顺序应该是:
var app = builder.Build();
// 1. 异常处理(放在最前面)
app.UseExceptionHandler("/error");
// 2. 身份验证
app.UseAuthentication();
app.UseAuthorization();
// 3. 静态文件
app.UseStaticFiles();
// 4. 路由
app.MapGet("/api/users", () => ...);
app.Run();
我曾经在一个生产项目中,把UseAuthentication放在了UseStaticFiles后面。结果呢?用户可以直接访问静态文件,绕过身份验证。嗯,那是个安全漏洞,还好在测试阶段发现了。
3.3.2 自定义中间件
有时候,你需要自己写中间件。比如,记录每个请求的处理时间:
app.Use(async (context, next) =>
{
var stopwatch = Stopwatch.StartNew();
await next(); // 调用下一个中间件
stopwatch.Stop();
Console.WriteLine($"请求 {context.Request.Path} 耗时 {stopwatch.ElapsedMilliseconds} ms");
});
这个中间件,在请求进来时启动计时器,等后续中间件处理完,再记录耗时。简单吧?
我个人习惯把自定义中间件封装成扩展方法,方便复用:
public static class RequestTimingMiddlewareExtensions
{
public static IApplicationBuilder UseRequestTiming(this IApplicationBuilder app)
{
return app.Use(async (context, next) =>
{
var stopwatch = Stopwatch.StartNew();
await next();
stopwatch.Stop();
Console.WriteLine($"请求 {context.Request.Path} 耗时 {stopwatch.ElapsedMilliseconds} ms");
});
}
}
// 使用
app.UseRequestTiming();
3.4 启动配置:环境与配置
ASP.NET Core的启动配置,支持多环境。开发、测试、生产,各有各的配置。
默认情况下,CreateBuilder会加载:
appsettings.json(通用配置)appsettings.{Environment}.json(环境特定配置)- 环境变量
- 命令行参数
配置的优先级是:命令行 > 环境变量 > 环境特定JSON > 通用JSON。
举个例子:
// appsettings.json
{
"Database": {
"ConnectionString": "Server=localhost;Database=MyDb;..."
}
}
// appsettings.Development.json
{
"Database": {
"ConnectionString": "Server=localhost;Database=MyDb_Dev;..."
}
}
在开发环境,Development配置会覆盖通用配置。这样,你就不用担心误连生产数据库了。
3.5 小结:从最小API到生产就绪
最小API虽然「最小」,但五脏俱全。它包含了ASP.NET Core的核心机制:
- Builder:服务注册和配置加载
- App:中间件管道和请求处理
- 中间件:请求的流水线处理
- 配置:多环境支持
你想想看,从三行代码开始,到支持身份验证、异常处理、静态文件、自定义中间件……这个演进过程,其实就是从「最小API」到「生产就绪」的过程。
下一章,我们会深入中间件管道,看看如何构建更复杂的请求处理流程。到时候,我会分享一些我在微服务架构中积累的中间件设计经验。
嗯,今天就到这里。动手试试吧,从dotnet new web开始,感受一下最小API的魅力。