CodeRoadMap
首页路线题库资源库文章社区
CodeRoadMap

程序员的学习成长路线图。内容免费开放,登录后可同步路线图与课时进度。

由 程序员柏风 独立维护 · 关于作者

学习

  • 路线图
  • 课程
  • 面试题
  • 文章
  • 我

更多

  • 资源
  • 社区
  • 关于作者
  • 内容说明

© 2026 CodeRoadMap

津ICP备2026012044号-1·津公网安备12011002026155号·coderoadmap@126.com
ASP.NET Core 实战入门/课时/ASP.NET Core 基础
RawBlame
课时目录12 节

ASP.NET Core 实战入门

  1. 01ASP.NET Core 基础
  2. 02C# 与 .NET 基础
  3. 03Entity Framework Core
  4. 04依赖注入
  5. 05REST / GraphQL / gRPC
  6. 06数据存储与缓存
  7. 07SignalR 实时通信
  8. 08测试与 E2E
  9. 09消息队列
  10. 10微服务与网关
  11. 11日志、任务与工具链
  12. 12综合练习

ASP.NET Core 基础

MVC、Minimal API、中间件与配置。

35 分钟

ASP.NET Core 概览与请求管道

这一课你将能做到

  1. 用 .NET 8 CLI 创建一个可运行的 Web 项目,并解释 Program.cs 每一行在做什么
  2. 说清请求管道(中间件洋葱模型)的执行顺序,并自己写一个自定义中间件
  3. 对照 Spring Boot 说出 ASP.NET Core 在启动流程、配置、请求处理上的异同

为什么要学

ASP.NET Core 是微软官方的跨平台 Web 框架,和 Spring Boot 一样负责「收请求、调业务、回响应」。你已经懂 Spring Boot 的过滤器链、自动配置、application.yml,那这套知识可以整体迁移过来,只需要重新映射一遍词汇表:中间件(Middleware)对应 Filter + Interceptor,WebApplicationBuilder 对应 SpringApplication,appsettings.json 对应 application.yml。

本站以 Java/Spring 技术栈为主线,但在线判题平台这类系统在后端语言选型上常会讨论 .NET(判题沙箱、并发吞吐都有竞争力)。掌握 ASP.NET Core,你在面试「双栈后端」岗位、以及理解框架设计共性上,都会有一个清晰的参照系。

先建立图像

一个 HTTP 请求穿过 ASP.NET Core 应用的路径像穿过洋葱,进去一层层剥,出来原路返回:

请求 →  异常处理 →  HTTPS重定向 →  路由匹配 →  端点执行
         │             │              │            │
响应 ←  补异常处理  ←  补HTTPS      ←  补路由    ←  返回结果

每层中间件都有机会在「请求进入前」和「响应返回后」各做一次事——这就是洋葱模型。Spring 的 Filter 链同样如此(doFilter 前后两段),但 ASP.NET Core 把它做成显式的委托管道,没有注解魔法。

启动流程对照:

Spring BootASP.NET Core职责
SpringApplication.run()WebApplication.CreateBuilder() + builder.Build()构建宿主
自动扫描 + 注解注册显式代码注册服务容器装配
Filter / HandlerInterceptorMiddleware横切管道
application.ymlappsettings.json配置
内嵌 Tomcat内嵌 KestrelWeb 服务器

跟着做

步骤 1:创建最小可运行项目

安装 .NET 8 SDK 后执行:

dotnet new web -o OjHealth
cd OjHealth
dotnet run

生成的 Program.cs 全部内容就是「顶级语句」——C# 12 允许不写 Main 方法,整个文件体就是入口:

var builder = WebApplication.CreateBuilder(args); // 1. 构建宿主:读配置、装 DI 容器
var app = builder.Build();                        // 2. 组装中间件管道

app.MapGet("/health", () => Results.Ok(new { status = "up" })); // 3. 注册端点

app.Run();                                        // 4. 启动 Kestrel,阻塞等待请求

浏览器访问 https://localhost:5001/health(端口看控制台输出),返回 {"status":"up"}。这一段代码等价于 Spring Boot 里一个 @RestController + 内嵌 Tomcat 启动的总和。

步骤 2:写一个自定义中间件

目标:给每个请求记录耗时,这是判题平台埋点的基础。

app.Use(async (context, next) =>
{
    var sw = Stopwatch.StartNew();
    await next(context);                    // 把请求交给下一层(洋葱进入内层)
    Console.WriteLine($"{context.Request.Path} 耗时 {sw.ElapsedMilliseconds}ms"); // 响应回来后执行
});

放在 app.MapGet(...) 之前。注意 await next() 这一行是分界线:之前是请求阶段,之后是响应阶段。漏掉 next 请求会在这里断掉,后面的路由永远收不到请求。

步骤 3:类形态的中间件(可复用、可注入)

public class TimingMiddleware(RequestDelegate next, ILogger<TimingMiddleware> logger)
{
    public async Task InvokeAsync(HttpContext context)
    {
        var sw = Stopwatch.StartNew();
        await next(context);
        logger.LogInformation("{Path} 耗时 {Ms}ms", context.Request.Path, sw.ElapsedMilliseconds);
    }
}

注册:app.UseMiddleware<TimingMiddleware>();。主构造函数(C# 12 新语法)直接省掉了构造函数样板——构造函数注入和 Spring 的 @RequiredArgsConstructor 一个思路。

步骤 4:配置绑定(对应 Spring 的 @ConfigurationProperties)

appsettings.json:

{
  "Judge": {
    "TimeoutSeconds": 5,
    "MaxConcurrency": 8
  }
}
public class JudgeOptions
{
    public int TimeoutSeconds { get; set; }
    public int MaxConcurrency { get; set; }
}

// Program.cs
builder.Services.Configure<JudgeOptions>(builder.Configuration.GetSection("Judge"));

// 任意服务中注入 IOptions<JudgeOptions> 即可拿到强类型配置

Spring 里这是 @ConfigurationProperties(prefix = "judge") + @EnableConfigurationProperties 的活,两者高度同构。

步骤 5:MVC 与 Minimal API 双形态

同一个接口,两种写法并存于 .NET 8:

// 形态 A:Minimal API(本课程示例多用这种,短平快)
app.MapGet("/api/problems/{id:int}", (int id) =>
    Results.Ok(new { id, title = "两数之和" }));

// 形态 B:Controller(大团队、大项目,结构更清晰)
[ApiController]
[Route("api/[controller]")]
public class ProblemsController : ControllerBase
{
    [HttpGet("{id:int}")]
    public IActionResult Get(int id) => Ok(new { id, title = "两数之和" });
}
// 注册:builder.Services.AddControllers(); app.MapControllers();

讲清楚要点

  1. 宿主(Host)是一切的地基:WebApplicationBuilder 负责装载配置源(环境变量 > appsettings.{Environment}.json > appsettings.json)、日志、DI 容器,然后 Build() 产出不可变的 WebApplication。这和 Spring 的 ApplicationContext 生命周期概念一致:先装配,后使用,装配完容器结构不再变。
  2. 中间件顺序就是契约:UseExceptionHandler → UseHttpsRedirection → UseAuthentication → UseAuthorization → MapXxx 的顺序不能乱。鉴权中间件放在路由后面,路由能匹配到但拿不到用户身份;放在 MapControllers 后面则鉴权根本不执行。
  3. Kestrel 是内置 Web 服务器:等价于内嵌 Tomcat,直接监听端口处理请求,生产环境前面通常再加 Nginx/YARP 做反向代理(对应 Spring 部署同样常见的 Nginx + Tomcat 结构)。
  4. 环境变量 ASPNETCORE_ENVIRONMENT:等价 Spring 的 spring.profiles.active,取值 Development / Staging / Production,代码里用 app.Environment.IsDevelopment() 分支。

你可能会懵的地方

  • 为什么 ASP.NET Core 不像 Spring 那样到处是注解? 设计哲学差异。Spring 靠注解 + 类路径扫描做「约定优于配置」;ASP.NET Core 8 更倾向显式代码注册,你在 Program.cs 一眼能看全所有装配关系。用泛型主机 + 第三方包(如 Autofac 的模块扫描)也能做出注解扫描的效果。

  • 中间件和 Controller 里常说的 Filter(如 [Authorize])是不是一回事? 不是。请求管道层的 Middleware 是框架级的、对所有请求生效;MVC 的 Filter(Action Filter、Resource Filter)和 Minimal API 的 Endpoint Filter 是「路由命中之后」的切面,粒度更细,更像 Spring 里 HandlerInterceptor 之于 Filter 的关系。

  • dotnet run 后控制台打印两个端口(http/https),监听规则在哪配置? launchSettings.json(开发期)、ASPNETCORE_URLS 环境变量或 UseUrls()。等价 Spring 的 server.port。

  • 顶级语句没有 Main 方法,程序入口到底是什么? 编译器把顶级语句文件合成为 Program.<Main>$。一个项目只允许一个文件使用顶级语句。这与 Java 21 草案的隐式类和 main 功能异曲同工。

  • Results.Ok() 这类返回值和 MVC 的 Ok() 什么关系? 同一协议(HTTP 语义),不同 API 门面。Minimal API 用静态类 Results,Controller 继承 ControllerBase 后用实例方法 Ok()。返回的都是 IResult / IActionResult,最终写回同一个响应流。

错法对照

错法问题正解
中间件里忘写 await next(...)管道短路,后续端点收不到请求除非刻意短路(如鉴权失败返回 401),必须调用 next
app.UseAuthorization() 放在 MapControllers() 之后鉴权不生效鉴权/授权中间件必须注册在端点映射之前
调 Build() 之后再改 DIWebApplication 装配后不可再 AddXxx所有 builder.Services.AddXxx 在 Build() 之前完成
修改代码不重启就刷新页面热加载范围有限,改 Program.cs 结构会失效dotnet watch 提供热重载;结构性修改保存后重启
app.UseRouting() 与 MapGet 顺序凭感觉写显式写 UseRouting/UseEndpoints 时路由时机错乱遵循 UseRouting → 中间件 → UseEndpoints 的固定顺序

卡点急救

  1. dotnet run 报证书错误(HTTPS):执行 dotnet dev-certs https --trust 安装并信任开发证书。
  2. 端口被占用:launchSettings.json 改 applicationUrl,或设置环境变量 ASPNETCORE_URLS=http://localhost:5100。
  3. 改了 appsettings.json 不生效:默认配置只在启动时读取一次;运行时刷新需要 IOptionsMonitor<T> / IOptionsSnapshot<T>(Scoped 内),不是 IOptions<T>。

面试常问

  • ASP.NET Core 的请求处理和 Spring Boot 的 Filter 链有何异同? 相同:都是责任链,请求按注册顺序穿入、响应按逆序穿出,前后两段可做横切逻辑。不同:ASP.NET Core 中间件是显式委托/类注册,顺序即语义且全局唯一一条链;Spring Filter 由容器按 @Order 排序,另有 MVC 层 Interceptor 二级切面。ASP.NET Core 对应 MVC 层还有 Action Filter 体系,双层结构两边一致。

  • Minimal API 会取代 Controller 吗? 不会互替,是场景分工。Minimal API 适合小服务、内部接口、原型(启动开销和样板更少);Controller 在模型绑定校验、版本管理、大团队分层上有成熟约定。.NET 8 里两者共享同一套路由、DI、过滤器基础设施,性能差异可忽略。

  • Kestrel 与 Nginx 的分工是什么? Kestrel 处理 HTTP 协议与应用逻辑,性能很高;生产环境让 Nginx 在前面做 TLS 终结、静态资源、负载均衡与缓冲慢客户端,Kestrel 只接内网流量。这与 Java 侧 Nginx + Tomcat 的经典拓扑完全同构。

自测三问

  1. CreateBuilder 到 app.Run() 之间发生了哪几件事?
  2. 一个请求被鉴权中间件拒绝时,洋葱模型里「响应阶段」的代码会执行吗?哪些中间件会执行?
  3. appsettings.Production.json 和环境变量同时配置同一键,谁赢?

小结一句话

ASP.NET Core 8 = 显式装配的宿主 + 洋葱式中间件管道 + 双形态端点(Minimal API / Controller),把 Spring Boot 的启动、配置、过滤链知识按词汇表映射过来,一天即可上手。

延伸

  • 官方文档:ASP.NET Core 中间件
  • 下一课:C# 12 与 .NET 8 语言基础——record、模式匹配、async/await,先补齐读代码的能力。
加载本节练习…
下一课 →C# 与 .NET 基础