第二章:项目骨架搭建

好,咱们开始动手了。

这一章,我会带你从零创建一个 ASP.NET Core Web API 项目。别小看这一步,骨架搭得好,后面写代码才舒服。我见过太多项目,一开始图省事,后面改得想哭。

2.1 创建第一个 ASP.NET Core Web API 项目

打开 Visual Studio 2022,或者你用 Rider 也行。我个人习惯用 .NET CLI,因为快,而且跨平台一致。

打开终端,敲下这行命令:

dotnet new webapi -n MyFirstApi

就这么简单。一个名为 MyFirstApi 的项目就生成了。

你可能会问:为什么不用模板里的「带控制器」选项?嗯,这里要注意,新版的 .NET 6+ 默认使用最小 API(Minimal API)。但咱们课程里,我选择用传统的控制器模式。为什么?因为真实企业项目里,控制器模式更常见,也更容易组织代码。

我曾经在一个创业项目里,一开始图新鲜全用最小 API,结果项目一复杂,路由散得到处都是,维护起来那叫一个酸爽。后来还是老老实实改回了控制器。

我的建议: 新手阶段,先用控制器模式。等你对路由、中间件都熟了,再玩最小 API 不迟。

2.2 项目结构解析

项目创建好了,咱们来看看里面都有啥。打开项目文件夹,你会看到这些:

  • Controllers/ —— 控制器放这里
  • Program.cs —— 应用程序入口
  • appsettings.json —— 配置文件
  • Properties/launchSettings.json —— 启动配置

别急着写代码,先搞清楚每个文件是干嘛的。

2.2.1 Program.cs —— 一切从这里开始

这是整个应用的入口。在 .NET 6 之后,它变得非常简洁。你看:

var builder = WebApplication.CreateBuilder(args);

// 注册服务
builder.Services.AddControllers();

var app = builder.Build();

// 配置中间件管道
app.MapControllers();

app.Run();

说白了,就三步:

  1. 创建 builder —— 准备构建应用
  2. 注册服务 —— 告诉框架你需要什么
  3. 构建并运行 —— 启动应用

我记得刚接触 .NET Core 时,看到 Startup.cs 和 Program.cs 分开,觉得好麻烦。后来 .NET 6 把它们合并了,确实清爽很多。但如果你是从 .NET 5 过来的,别慌,后面我会讲 Startup 的兼容写法。

2.2.2 launchSettings.json —— 启动的秘密

这个文件藏在 Properties 文件夹里。很多人忽略它,但它决定了你的项目怎么跑。

{
  "profiles": {
    "MyFirstApi": {
      "commandName": "Project",
      "dotnetRunMessages": true,
      "launchBrowser": true,
      "applicationUrl": "https://localhost:5001;http://localhost:5000",
      "environmentVariables": {
        "ASPNETCORE_ENVIRONMENT": "Development"
      }
    }
  }
}

这里有几个关键点:

  • applicationUrl —— 应用监听的地址和端口。默认是 5000/5001
  • ASPNETCORE_ENVIRONMENT —— 环境变量。Development 表示开发环境
  • launchBrowser —— 启动时是否自动打开浏览器

我曾经踩过一个坑:把 applicationUrl 改成了 8080,结果忘了改回来,部署到服务器上端口冲突,排查了半天。所以,这个文件虽然小,但改的时候要小心。

注意: launchSettings.json 只在开发环境生效。部署到生产环境时,端口和地址要通过环境变量或命令行参数设置。

2.3 依赖注入初体验

依赖注入(DI)是 ASP.NET Core 的核心。说白了,就是「我要什么,你给我什么」,不用我自己 new。

举个例子。假设你有一个日志服务:

public interface ILoggerService
{
    void Log(string message);
}

public class ConsoleLoggerService : ILoggerService
{
    public void Log(string message)
    {
        Console.WriteLine($"[{DateTime.Now}] {message}");
    }
}

在 Program.cs 里注册它:

builder.Services.AddScoped<ILoggerService, ConsoleLoggerService>();

然后在控制器里直接用:

[ApiController]
[Route("api/[controller]")]
public class WeatherController : ControllerBase
{
    private readonly ILoggerService _logger;

    public WeatherController(ILoggerService logger)
    {
        _logger = logger;
    }

    [HttpGet]
    public IActionResult Get()
    {
        _logger.Log("有人调用了 Get 方法");
        return Ok(new { Message = "Hello World" });
    }
}

看到没?控制器不需要知道具体是哪个日志实现。你只需要在构造函数里声明「我需要一个 ILoggerService」,框架会自动给你注入。

这就是依赖注入的魅力——解耦。你想想看,如果以后想换成文件日志,只需要改注册那一行代码,控制器完全不用动。

三种生命周期:
生命周期 说明 适用场景
AddTransient 每次请求都创建新实例 轻量级、无状态服务
AddScoped 每个 HTTP 请求内共享同一个实例 数据库上下文、业务服务
AddSingleton 整个应用生命周期内只有一个实例 配置、缓存、日志

我在项目中遇到过一个问题:把一个 Scoped 服务注入到了 Singleton 服务里。结果呢?Singleton 一直持有同一个 Scoped 实例,导致数据错乱。排查了好久才发现是生命周期搞错了。

所以,记住一个原则:不要注入生命周期比自己短的服务。Singleton 里不要注入 Scoped 或 Transient,Scoped 里不要注入 Transient(除非你知道自己在做什么)。

2.4 本章小结

这一章,我们做了三件事:

  • 用 CLI 创建了一个 Web API 项目
  • 搞清楚了 Program.cs 和 launchSettings.json 的作用
  • 体验了依赖注入的基本用法

骨架已经搭好了。下一章,咱们开始往里面填肉——写真正的业务代码。

对了,如果你在创建项目时遇到问题,检查一下 .NET SDK 版本。用 dotnet --version 看看,至少要是 6.0 以上。

小技巧: 每次新建项目后,先跑一下 dotnet run,确保能正常启动。别等到写了一大堆代码才发现环境有问题。

好,这一章就到这儿。有问题随时问,咱们下章见。