1.項目創建
使用Visual Studio 2019 創建一個Asp.NetCore WebApplication 並選擇MVC項目命名為OnePublish。

確認運行無誤后我們來探討一下Asp.NetCore項目的運行啟動方式以及他與Asp.Net的區別。
2.項目運行
IIS Express運行效果:

結果不出乎意料和Asp.Net相似都能成功的運行Web項目,但在Asp.NetCore中新增了一種使用CommandLine運行的方式,我們可以從配置文件或者VS設置中可以看出。
項目屬性設置:

LaunchSettings.json
1 { 2 "iisSettings": { 3 "windowsAuthentication": false, 4 "anonymousAuthentication": true, 5 "iisExpress": { 6 "applicationUrl": "http://localhost:54012", 7 "sslPort": 0 8 } 9 }, 10 "profiles": { 11 "IIS Express": { 12 "commandName": "IISExpress", 13 "launchBrowser": true, 14 "environmentVariables": { 15 "ASPNETCORE_ENVIRONMENT": "Development" 16 } 17 }, 18 "OnePublish": { 19 "commandName": "Project", 20 "launchBrowser": true, 21 "applicationUrl": "http://localhost:5000", 22 "environmentVariables": { 23 "ASPNETCORE_ENVIRONMENT": "Development" 24 } 25 } 26 } 27 }
我們可以看見以"OnePublish"為名的CommandLine也可以成功運行Web項目,那Asp.NetCore是如何做到從命令行實現Web項目的呢?
原因在於ASP.NET Core不再是由IIS工作進程(w3wp.exe)托管,而是使用自托管Web服務器(Kestrel)運行,IIS則是作為反向代理的角色轉發請求到Kestrel不同端口的ASP.NET Core程序中,隨后就將接收到的請求推送至中間件管道中去,處理完你的請求和相關業務邏輯之后再將HTTP響應數據重新回寫到IIS中,最終轉達到不同的客戶端(瀏覽器,APP,客戶端等)。
官方文檔圖解:
Kestrel used as an edge (Internet-facing) web server:

Kestrel used in a reverse proxy configuration:

我的理解為:當Kestrel作為Eage Server監聽某一個ip或者端口時,它可以接受所有由該端口發送的信息並解決他們的請求。但當有反向代理的存在時如(IIS,Nginx,Apache),反向代理可以接受多個Kestrel托管的端口來接受他們的信息並完成他們的請求。總結來說在Asp.NetCore中Kestrel已經組成一個完整的生態,而代理服務器大多起一個監聽的作用了。那Kestrel在哪里啟動的呢?
首先我們來觀察一下這兩個類文件。

Program.cs:
1 using Microsoft.AspNetCore.Hosting; 2 using Microsoft.Extensions.Hosting; 3 4 namespace OnePublish 5 { 6 public class Program 7 { 8 public static void Main(string[] args) 9 { 10 CreateHostBuilder(args).Build().Run(); 11 } 12 13 public static IHostBuilder CreateHostBuilder(string[] args) => 14 Host.CreateDefaultBuilder(args) 15 .ConfigureWebHostDefaults(webBuilder => 16 { 17 webBuilder.UseStartup<Startup>(); 18 }); 19 } 20 }
我們知道Main函數一般為程序的入口。在此函數中我們可以看見是調用了一系列方法,這可能還不夠簡單明了,我們對此方法做一點簡化。
1 public static void Main(string[] args) 2 { 3 var hostBuilder = CreateHostBuilder(args); 4 var host = hostBuilder.Build(); 5 host.Run(); 6 }
現在我們可以理解為它使用CreateHsotBuilder()方法創建了一個hostBuilder,在利用hostBuilder創建了一個host主機實體對象,在Run這個Host主機。
那么這個HostBuiler到底做了什么配置又創建了一些什么東西呢?
我們對源碼進行解讀一下:
1 public static IHostBuilder CreateDefaultBuilder(string[] args) 2 { 3 HostBuilder hostBuilder = new HostBuilder(); 4 hostBuilder.UseContentRoot(Directory.GetCurrentDirectory()); 5 hostBuilder.ConfigureHostConfiguration((Action<IConfigurationBuilder>) (config => 6 { 7 config.AddEnvironmentVariables("DOTNET_"); 8 if (args == null) 9 return; 10 config.AddCommandLine(args); 11 })); 12 hostBuilder.ConfigureAppConfiguration((Action<HostBuilderContext, IConfigurationBuilder>) ((hostingContext, config) => 13 { 14 IHostEnvironment hostingEnvironment = hostingContext.HostingEnvironment; 15 config.AddJsonFile("appsettings.json", true, true).AddJsonFile("appsettings." + hostingEnvironment.EnvironmentName + ".json", true, true); 16 if (hostingEnvironment.IsDevelopment() && !string.IsNullOrEmpty(hostingEnvironment.ApplicationName)) 17 { 18 Assembly assembly = Assembly.Load(new AssemblyName(hostingEnvironment.ApplicationName)); 19 if (assembly != (Assembly) null) 20 config.AddUserSecrets(assembly, true); 21 } 22 config.AddEnvironmentVariables(); 23 if (args == null) 24 return; 25 config.AddCommandLine(args); 26 })).ConfigureLogging((Action<HostBuilderContext, ILoggingBuilder>) ((hostingContext, logging) => 27 { 28 int num = RuntimeInformation.IsOSPlatform(OSPlatform.Windows) ? 1 : 0; 29 if (num != 0) 30 logging.AddFilter<EventLogLoggerProvider>((Func<LogLevel, bool>) (level => level >= LogLevel.Warning)); 31 logging.AddConfiguration((IConfiguration) hostingContext.Configuration.GetSection("Logging")); 32 logging.AddConsole(); 33 logging.AddDebug(); 34 logging.AddEventSourceLogger(); 35 if (num == 0) 36 return; 37 logging.AddEventLog(); 38 })).UseDefaultServiceProvider((Action<HostBuilderContext, ServiceProviderOptions>) ((context, options) => 39 { 40 bool flag = context.HostingEnvironment.IsDevelopment(); 41 options.ValidateScopes = flag; 42 options.ValidateOnBuild = flag; 43 })); 44 return (IHostBuilder) hostBuilder; 45 }
可以看到它對路徑做了一個默認的規定,並的確的使用的CommandLine並可接受參數,這也是為什么我們可以通過CommandLine控制Web程序的原因,還有對appsettings.json文件的引用,從這里我們可以看出我們也是可以自定義配置文件的。還添加了Logging這些基本功能。
繼續向源碼解讀可以看到在ConfigureWebDefaults方法中,Asp.NetCore確實使用了Kestrel並對他的一些option進行了默認的規定。到這里我們就基本了明白了Asp.NetCore WebApplication的啟動流程了。
internal static void ConfigureWebDefaults(IWebHostBuilder builder) { builder.ConfigureAppConfiguration((Action<WebHostBuilderContext, IConfigurationBuilder>) ((ctx, cb) => { if (!ctx.HostingEnvironment.IsDevelopment()) return; StaticWebAssetsLoader.UseStaticWebAssets(ctx.HostingEnvironment, ctx.Configuration); })); builder.UseKestrel((Action<WebHostBuilderContext, KestrelServerOptions>) ((builderContext, options) => options.Configure((IConfiguration) builderContext.Configuration.GetSection("Kestrel")))).ConfigureServices((Action<WebHostBuilderContext, IServiceCollection>) ((hostingContext, services) => { services.PostConfigure<HostFilteringOptions>((Action<HostFilteringOptions>) (options => { if (options.AllowedHosts != null && options.AllowedHosts.Count != 0) return; string str = hostingContext.Configuration["AllowedHosts"]; string[] strArray1; if (str == null) strArray1 = (string[]) null; else strArray1 = str.Split(new char[1]{ ';' }, StringSplitOptions.RemoveEmptyEntries); string[] strArray2 = strArray1; HostFilteringOptions filteringOptions = options; string[] strArray3; if (strArray2 == null || strArray2.Length == 0) strArray3 = new string[1]{ "*" }; else strArray3 = strArray2; filteringOptions.AllowedHosts = (IList<string>) strArray3; })); services.AddSingleton<IOptionsChangeTokenSource<HostFilteringOptions>>((IOptionsChangeTokenSource<HostFilteringOptions>) new ConfigurationChangeTokenSource<HostFilteringOptions>(hostingContext.Configuration)); services.AddTransient<IStartupFilter, HostFilteringStartupFilter>(); if (string.Equals("true", hostingContext.Configuration["ForwardedHeaders_Enabled"], StringComparison.OrdinalIgnoreCase)) { services.Configure<ForwardedHeadersOptions>((Action<ForwardedHeadersOptions>) (options => { options.ForwardedHeaders = ForwardedHeaders.XForwardedFor | ForwardedHeaders.XForwardedProto; options.KnownNetworks.Clear(); options.KnownProxies.Clear(); })); services.AddTransient<IStartupFilter, ForwardedHeadersStartupFilter>(); } services.AddRouting(); })).UseIIS().UseIISIntegration(); }
3.區別:
在我看來Asp.Net就像一個全家桶,在我默認使用框架中基本上包含了所有進行Web開發的框架如HttpContext,Session等。而Asp.NetCore更像是一個自選桶你可以在StartUp.cs中自定義的添加你需要的Services。在我看來,Asp.NetCore變得更加透明和更加靈活了。
4.部署和發布。
1. 我們將此項目用文件系統打包部署到本地即可。點擊Publish即可。


2. 確保打開Windows的IIS服務。

確保IIS中安裝了AspNetCoreModule.

若無安裝(去安裝):下載安裝即可

現在來添加IIS服務



運行成功,即發布成功了。
若不能成功運行請檢查部署目錄中是否有此文件。


