#author("2023-11-19T11:38:30+08:00","default:Admin","Admin") #author("2024-01-24T22:42:25+08:00","default:Admin","Admin") [[ASP.NET Core Web]] &color(red){※This article is based on .NET 7}; #contents * 概要 [#w9a1afa4] 标准提供 WebApplication 和 WebApplicationBuilder 两个类,从的名字上大致也能猜测到 - WebApplicaiton代表的是运行的应用 - ApplicationBuilder 意味着由它构建的就是一个Application,是为了运行该应用的配置类。 那么在ASP.NET Core框架的语义下应用(Application)可以这样来理解:既然Pipeline = Server + HttpHandler,那么用来处理请求的HttpHandler不就承载了当前应用的所有职责吗? 那么HttpHandler就等于Application,由于HttpHandler通过RequestDelegate表示,那么由ApplicationBuilder构建的Application就是一个RequestDelegate对象。 由于表示HttpHandler的RequestDelegate是由注册的中间件来构建的,所以ApplicationBuilder还具有注册中间件的功能。基于ApplicationBuilder具有的这两个基本职责,我们可以将对应的接口定义成如下的形式。Use方法用来注册提供的中间件,Build方法则将注册的中间件构建成一个RequestDelegate对象。 #codeprettify{{ public interface IApplicationBuilder { IApplicationBuilder Use(Func<RequestDelegate, RequestDelegate> middleware); RequestDelegate Build(); } }} 如下所示的是针对该接口的具体实现。我们利用一个列表来保存注册的中间件,所以Use方法只需要将提供的中间件添加到这个列表中即可。当Build方法被调用之后,我们只需按照与注册相反的顺序依次执行表示中间件的Func<RequestDelegate, RequestDelegate>对象就能最终构建出代表HttpHandler的RequestDelegate对象。 #codeprettify{{ public class ApplicationBuilder : IApplicationBuilder { private readonly List<Func<RequestDelegate, RequestDelegate>> _middlewares = new List<Func<RequestDelegate, RequestDelegate>>(); public RequestDelegate Build() { _middlewares.Reverse(); return httpContext => { RequestDelegate next = _ => { _.Response.StatusCode = 404; return Task.CompletedTask; }; foreach (var middleware in _middlewares) { next = middleware(next); } return next(httpContext); }; } public IApplicationBuilder Use(Func<RequestDelegate, RequestDelegate> middleware) { _middlewares.Add(middleware); return this; } } }} 在调用第一个中间件(最后注册)的时候,我们创建了一个RequestDelegate作为输入,后者会将响应状态码设置为404。所以如果http://ASP.NET Core应用在没有注册任何中间的情况下总是会返回一个404的响应。如果所有的中间件在完成了自身的请求处理任务之后都选择将请求向后分发,同样会返回一个404响应。 ** 标准默认做法 [#xa976650] Startup.cs #codeprettify{{ namespace WebApplication1 { public class Startup { public Startup(IConfiguration configuration) { Configuration = configuration; } public IConfiguration Configuration { get; } // This method gets called by the runtime. Use this method to add services to the container. public void ConfigureServices(IServiceCollection services) { services.AddControllers(); services.AddSwaggerGen(c => { c.SwaggerDoc("v1", new OpenApiInfo { Title = "WebApplication1", Version = "v1" }); }); } // This method gets called by the runtime. Use this method to configure the HTTP request pipeline. public void Configure(IApplicationBuilder app, IWebHostEnvironment env) { if (env.IsDevelopment()) { app.UseDeveloperExceptionPage(); app.UseSwagger(); app.UseSwaggerUI(c => c.SwaggerEndpoint("/swagger/v1/swagger.json", "WebApplication1 v1")); } app.UseHttpsRedirection(); app.UseRouting(); app.UseAuthorization(); app.UseEndpoints(endpoints => { endpoints.MapControllers(); }); } } } }} * 极简代码 [#cafd5c2a] 通过代码来配置 #codeprettify{{ var builder = WebApplication.CreateBuilder(args); // Configure the cert and the key builder.Configuration["Kestrel:Certificates:Default:Path"] = "cert.pem"; builder.Configuration["Kestrel:Certificates:Default:KeyPath"] = "key.pem"; var app = builder.Build(); app.Urls.Add("https://localhost:3000"); app.MapGet("/", () => "Hello World"); app.Run(); }} * 配置 [#j3f28966] ** 改变应用content root, app name以及环境变量 [#pc9169a8] #codeprettify{{ var builder = WebApplication.CreateBuilder(new WebApplicationOptions { ApplicationName = typeof(Program).Assembly.FullName, ContentRootPath = Directory.GetCurrentDirectory(), EnvironmentName = Environments.Staging }); Console.WriteLine($"Application Name: {builder.Environment.ApplicationName}"); Console.WriteLine($"Environment Name: {builder.Environment.EnvironmentName}"); Console.WriteLine($"ContentRoot Path: {builder.Environment.ContentRootPath}"); var app = builder.Build(); }} 运行后: #codeprettify{{ :\MyProjects\DotNetLearning\.Net6\WebApplicationDemo>dotnet run 正在生成... Application Name: WebApplicationDemo, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null Environment Name: Staging ContentRoot Path: D:\MyProjects\DotNetLearning\.Net6\WebApplicationDemo }} 除了代码改变之外,还可以通过如下的环境变量来改变: - ASPNETCORE_ENVIRONMENT - ASPNETCORE_CONTENTROOT - ASPNETCORE_APPLICATIONNAME 或者通过命令行参数来改变: - --applicationName - --environment - --contentRoot ** 配置项 [#rf6a9fc1] *** 添加配置source [#mab44dac] #codeprettify{{ var builder = WebApplication.CreateBuilder(args); builder.Configuration.AddIniFile("appsettings.ini"); var app = builder.Build(); }} 读取配置 默认按照如下的顺序读取配置: + appSettings.json + appSettings.{environment}.json + environment variables + 命令行参数 *** 读取配置 [#v24d3b22] 读取配置的实例: #codeprettify{{ var builder = WebApplication.CreateBuilder(args); // Reads the ConnectionStrings section of configuration and looks for a sub key called Todos var connectionString = builder.Configuration.GetConnectionString("Todos"); Console.WriteLine($"My connection string is {connectionString}"); var app = builder.Build(); }} appsettings.json的内容 #codeprettify{{ { "cnblogs": "My CNBlogs Address:https://www.cnb.com", "AppSettings": { "HttpUrl": "http://www.gt.com", "Copyright": "gzbit" }, "Person": [ { "name": "xfmtl", "company": "gzbit", "address": [ { "province": "Guizhou Province", "city": "Guiyang City" }, { "province": "Yunnan Province", "city": "Wuhua District" } ] }, { "name": "C# Study", "adress": "Microsoft" } ], "Book": { "name": "C# Program", "publish": "2020-08" } } }} Controller里读取配置的实例: #codeprettify{{ public IActionResult Index() { //加载配置文件 ConfigurationBuilder configurationBuilder = new ConfigurationBuilder(); //添加配置文件路径 configurationBuilder.SetBasePath(Directory.GetCurrentDirectory()) .AddJsonFile("appsettings.json"); var configuration = configurationBuilder.Build(); //获取博客园地址 var cnblogs = configuration["cnblogs"]; //获取Book名称 var bookname = configuration.GetValue<string>("Book:name"); //获取幸福摩天轮的第一地区名称 var addressName = configuration.GetValue<string>("Person:0:address:1:city"); //获取C# 学习的名称 var cname = configuration.GetValue<string>("Person:1:name"); //获取所在公司 var companyName= configuration["AppSettings:Copyright"]; return Content(cnblogs); } }} *** 读取环境变量 [#rc3d0ba1] #codeprettify{{ var builder = WebApplication.CreateBuilder(args); if (builder.Environment.IsDevelopment()) { Console.WriteLine($"Running in development"); } var app = builder.Build(); }} *** 添加日志配置 [#fb363e5f] #codeprettify{{ var builder = WebApplication.CreateBuilder(args); // Configure JSON logging to the console builder.Logging.AddJsonConsole(); var app = builder.Build(); }} *** 添加Service [#ub8efc2d] #codeprettify{{ var builder = WebApplication.CreateBuilder(args); // Add the memory cache services builder.Services.AddMemoryCache(); // Add a custom scoped service builder.Services.AddScoped<ITodoRepository, TodoRepository>(); var app = builder.Build(); }} *** 改变webroot [#r8da6aca] 默认情况下webroot是在相对路径下的wwwroot下,如果要改变这个路径,可以使用如下的方法: #codeprettify{{ var builder = WebApplication.CreateBuilder(args); // Look for static files in webroot builder.WebHost.UseWebRoot("webroot"); var app = builder.Build(); }} *** 定制DI容器 [#de11d13e] 我们这里使用Autofac #codeprettify{{ var builder = WebApplication.CreateBuilder(args); builder.Host.UseServiceProviderFactory(new AutofacServiceProviderFactory()); // Register your own things directly with Autofac here. Don't // call builder.Populate(), that happens in AutofacServiceProviderFactory // for you. builder.Host.ConfigureContainer<ContainerBuilder>(builder => builder.RegisterModule(new MyApplicationModule())); var app = builder.Build(); }} *** 添加中间件 [#ndd26a13] 中间件的添加都应该在WebApplication上完成 #codeprettify{{ var app = WebApplication.Create(args); // Setup the file server to serve static files app.UseFileServer(); app.Run(); }} *** 开发的错误页面 [#q36c784c] WebApplication默认启用了开发者异常页面 #codeprettify{{ var app = WebApplication.Create(args); app.MapGet("/", () => throw new InvalidOperationException("Oops, the '/' route has thrown an exception.")); app.Run(); }} #hr(); コメント: #comment_kcaptcha }}