Skip to content
横幅:外观模式:ASP.NET Core 里那些让你“无痛”写代码的秘密武器

外观模式:ASP.NET Core 里那些让你“无痛”写代码的秘密武器 ​

打开一个 ASP.NET Core 项目,你会看到什么?

csharp
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddControllers();
var app = builder.Build();
app.MapControllers();
app.Run();

五句话,一个 Web 应用就搭起来了。

但你想过没有——背后那成千上万行的依赖注入容器、中间件管道、宿主构建、请求上下文……这些复杂得要命的东西,谁帮你搞定的?

答案是一个设计模式:外观模式(Facade)。

它就像你家里的智能遥控器。按下“观影模式”,窗帘自动拉上、灯光调暗、电视打开——你完全不用管背后的红外信号、电路逻辑、设备协议。你只管按按钮,剩下的交给遥控器。

ASP.NET Core 里到处都是这种“遥控器”。今天我们就来拆开看看,它们到底是怎么工作的。

外观模式是啥?一个前台接待员 ​

简单说,外观模式就是给一群复杂的“幕后人员”配一个前台接待。你只需要找前台,前台帮你搞定一切。

GoF 的定义里有三个角色:

  • 外观:统一接口,就像前台小姐姐
  • 子系统:真正干活的部门,比如财务、人事、技术
  • 客户端:你,只跟前台说话

核心思想就四个字:封装复杂,暴露简单。

ASP.NET Core 深谙此道,几乎把整个框架都“包装”成了一个个外观。你感觉不到复杂,是因为有人替你扛了。

IServiceCollection:点菜一样注册服务 ​

如果没有外观,注册一个服务要怎么做?

你得手动创建 ServiceDescriptor 列表,操心生命周期怎么存,还要自己构建 ServiceProvider……光是想想就头大。

但现在呢?

csharp
var services = new ServiceCollection();
services.AddLogging();
services.AddControllers();
services.AddScoped<IUserService, UserService>();
var provider = services.BuildServiceProvider();

这几行代码背后藏着什么?ServiceDescriptor 的集合管理、ServiceProvider 的表达式树编译、作用域验证、循环依赖检测……但作为开发者的你,完全不用管。

IServiceCollection 就是那个前台。你告诉她“我要加日志,加 MVC,加一个 UserService”,她扭头就帮你办妥了。

IApplicationBuilder:搭中间件管道,像搭积木 ​

ASP.NET Core 的请求处理管道,本质上是一串 Func<RequestDelegate, RequestDelegate> 委托链。你要直接去拼这些委托,眼睛都能看花。

但 IApplicationBuilder 给了你一套亲切的 API:

csharp
app.UseRouting();
app.UseAuthentication();
app.UseAuthorization();
app.UseEndpoints(endpoints => endpoints.MapControllers());

每个 UseXXX 都是外观方法。内部帮你把中间件包装成委托,挂到管道上。你不用操心作用域、依赖注入怎么传递——外观全包了。

就像搭积木,每一块放上去,管道就自动成型。

WebApplication:从“古典”到“极简”的飞跃 ​

如果你经历过 .NET Core 3.x 时代,应该还记得那个“启动三件套”:

csharp
public class Program
{
    public static void Main(string[] args) =>
        CreateHostBuilder(args).Build().Run();

    public static IHostBuilder CreateHostBuilder(string[] args) =>
        Host.CreateDefaultBuilder(args)
            .ConfigureWebHostDefaults(webBuilder =>
            {
                webBuilder.UseStartup<Startup>();
            });
}

繁琐得像写八股文。

而到了 .NET 6+,WebApplication 横空出世,把宿主、配置、日志、中间件、服务器全部糅合成一个外观:

csharp
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddControllers();
var app = builder.Build();
app.MapControllers();
app.Run();

五句话,搞定一切。你看到的是 5 行代码,实际上是 IHostBuilder + IApplicationBuilder + IConfiguration + 日志 + Kestrel 的“全家桶”。

WebApplication 就是那个万能遥控器,一键启动所有子系统。而且它没有锁死扩展性——你想动底层,依然可以通过 builder.Services 或 app 钻进去调细节。

HttpContext:一个对象,装下整个 HTTP 世界 ​

在中间件或控制器里,你要拿请求、写响应、看用户身份、读写 Session……如果每个都去单独找底层对象,代码会变得无比丑陋。

HttpContext 就是为你准备好的“百宝箱”:

csharp
app.Run(async context =>
{
    var request = context.Request;
    var response = context.Response;
    var user = context.User;
    var session = context.Session;
    await response.WriteAsync($"Hello {user.Identity.Name}");
});

你不需要知道 IFeatureCollection 里藏着什么 IHttpRequestFeature,也不用管协议解析细节。HttpContext 把这些子系统打包成几个直观的属性,让你用起来像在翻自己的背包。

ControllerBase:把 MVC 的脏活累活全包了 ​

在 MVC 里,你写一个 ProductsController:

csharp
[ApiController]
[Route("api/[controller]")]
public class ProductsController : ControllerBase
{
    [HttpGet("{id}")]
    public async Task<ActionResult<ProductDto>> Get(int id)
    {
        var product = await _productService.GetByIdAsync(id);
        if (product == null) return NotFound();
        return Ok(product);
    }
}

你有没有想过——谁帮你做的路由匹配?谁把 URL 里的 id 绑定到参数?谁验证 [Required] 特性?谁把 Ok() 转成 200 响应?

全是 MVC 内部子系统干的事。但 ControllerBase 这个外观把它们全挡在了外面。你只需要专注于业务逻辑,剩下的交给“遥控器”。

IWebHostEnvironment:不用自己查字典了 ​

以前要判断是不是开发环境,你可能得读环境变量 ASPNETCORE_ENVIRONMENT,还要处理不同操作系统的路径分隔符。

现在注入一个 IWebHostEnvironment,世界就清净了:

csharp
if (_env.IsDevelopment())
{
    Console.WriteLine($"Content root: {_env.ContentRootPath}");
}

它帮你缓存了环境变量、统一了路径抽象,还给你一堆好用的扩展方法。这就是外观的温柔。

外观 vs 适配器 vs 中介者,别搞混了 ​

有朋友可能问:这不就是适配器吗?

区别很大:

模式目标比喻
外观简化接口翻译官——让你听懂复杂术语
适配器转换接口转换插头——让英标插头插进国标插座
中介者协调依赖居委会——协调邻里纠纷

目标不同,别用岔了。

外观模式在 ASP.NET Core 中的全景 ​

回顾一下,框架里的外观无处不在:

外观对象帮你搞定了什么?
IServiceCollection服务注册的繁琐管理
IApplicationBuilder中间件委托链的构建
WebApplication宿主 + 配置 + 日志 + 服务器,一键启动
HttpContext请求 / 响应 / 用户 / 会话等子对象的聚合
ControllerBase模型绑定、验证、路由、结果执行
IWebHostEnvironment环境变量和路径的读取与缓存

这些外观加在一起,构成了 ASP.NET Core 的“易用性底座”。

写给自己的思考 ​

理解外观模式,你不仅能更深刻地领会框架的设计哲学,更能在自己的代码里借鉴这种思路。

当你觉得某个模块用起来很“拧巴”时,试着给它套一层外观。

把复杂性藏到背后,把简单的 API 留给调用者。这正是 ASP.NET Core 成功的秘诀之一:强大,但不折磨人。

下次你再用 app.MapControllers() 的时候,记得在心里默默感谢一下那个替你在背后扛事的外观。然后想想——你自己的代码里,有没有哪个复杂子系统,也值得配一个这样的“前台接待”?

Released under the MIT License.