[译]ASP.NET Core 2.0 入门教程

原文:ASP.NET Core - Getting Started with ASP.NET Core 2.0

ASP.NET Core 能够创建快速的,便捷的,跨平台的web应用。这篇文章通过一个简单的ASP.NET Core web站点,向你展示工程中的每个文件的作用,同时也将阐述ASP.NET Core的一些重要概念。还将特别关注ASP.NET Core 2.0 的改变,以帮助熟悉ASP.NET Core 1.0 和 1.1 的读者过度到2.0。

创建ASP.NET Core 工程

ASP.NET Core 工程可以通过Visual Studio模板或.NET Core 命令行接口(.NET CLI)创建。Visual Studio 2017有极好的 .NET Core 开发体验(包括顶级的代码调试、Docker集成以及其他功能),但是在这里,我将使用.NET Core命令行和Visual Studio Code,以防有些读者使用Mac或Linux的开发机。

dotnet new 命令用来创建新的.NET Core工程。当运行 dotnet new 而不加参数,命令行会列出可以使用的工程模板,如图1所示。如果你熟悉2.0版本之前的.NET CLI,你将会注意到2.0加入了一些新的工程模板。

图1 新工程模板

Angular和React.js SPA模板: 将创建一个ASP.NET Core 的应用程序,为前端的SPA程序(使用Angular 4 或 React.js)提供服务。这些模板包括前端和后端的程序,以及用于构建前端的Webpack的配置。

ASP.NET Core Web App (Razor Pages): Razor页面是ASP.NET Core 2.0的一个新特性,它能够创建直接处理请求的页面,而不需要Controller。它非常适合基于页面的程序模板。

让我们用 dotnet new razor 命令创建一个Razor页面的工程。当这个工程被创建后,你可以执行 dotnet run 命令运行它。在.NET Core 之前的版本,需要执行先 dotnet restore 命令来安装必须的NuGet包。但是从.NET Core 2.0 开始,使用CLI命令时,restore命令会自动执行。运行这个站点并输入app的URL地址(如: http://localhost:5000 ),你将看到这个web app(图2)。

图2 一个简单的ASP.NET Core App

恭喜你完成了第一个ASP.NET Core 2.0 的一个App!现在你运行了一个简单的web app,让我们看一看这个工程的内容,以便你更了解ASP.NET Core是怎样工作的。

依赖,源文件和资源

第一个要看的是工程文件本身----.csproj文件。这个文件告诉MSBuild怎样构建这个工程,依赖的包,.NET Core 的目标版本等等。如果你看过以前版本的.csproj文件,你会发现2.0版本的这个文件非常小。社区做了很多的努力,使得.csproj文件简短且已读。一个显著的改变是,源文件不在显示的被列在工程文件中。取而代之的是.NET Core 会自动编译所有.cs文件。同样,任何.resx文件都作为资源而被包含。如果你不希望所有的.cs文件都被编译,您可以从Compile ItemGroup中删除它们,也可以通过将EnableDefaultCompileItems属性设置为false来完全禁用默认编译项。

app运行的 .NET 版本由 <TargetFramework> 元素指定。这里为了使用ASP.NET Core 2.0的新特性,设置为了netcoreapp2.0。

.csproj文件中的 <PackageReference> 元素表示工程中依赖的NuGet包。在ASP.NET Core 2.0 中,你会注意到它默认只包含了一个数据(Microsoft.AspNetCore.All)。这个package包含了所有其它的Microsoft.AspNetCore的包,这样使得ASP.NET Core 2.0 的工程文件比起之前的短小了很多。其它的NuGet依赖包可以通过Visual Studio NuGet Package插件的管理界面或者.NET CLI 中 dotnet add 命令添加<PackageReference>元素。

如果你使用了SPA模板(angular,react或reactredux),还会在.csproj文件中定义标签以确保在构建工程的时候同时运行Webpack。

创建和运行Web Host

Program.cs 是整个程序的入口点。ASP.NET Core 的应用是一个console application,像所有的控制台应用一样,当app运行时,会运行Main方法。

在ASP.NET Core 2.0模板中,Main方法相当简单,就是创建了一个IWebHost对象,然后调用了Run方法。

public class Program
{
    public static void Main(string[] args)
    {
        CreateWebHostBuilder(args).Build().Run();
    }

    public static IWebHostBuilder CreateWebHostBuilder(string[] args) =>
        WebHost.CreateDefaultBuilder(args)
            .UseStartup<Startup>();
}

如果你以前使用过1.0版本,你会注意到现在这个文件更简单了。原因是WebHost.CreateDefaultBuilder这个新方法。以前,ASP.NET Core 应用程序的Main方法为了创建IWebHost的实例需要配置 WebHostBuilder,这个配置包含了一些步骤,像指定web server,设置content root路径,以及运行IIS集成等。

CreateDefaultBuilder创建了一个默认配置的IWebHost,以简化以上的步骤。除了前面指定的项,CreateDefaultBuilder还做了一些在以前版本中Startup.cs类中设置的事(设置配置信息,注册默认的logging providers)。因为ASP.NET Core是开源的,如果你感兴趣,你可以在GitHub WebHost.cs上看到CreateDefaultBuilder做的所有细节。

让我们简要的看一看在CreateDefaultBuilder中重要的调用以及其目的。虽然CreateDefaultBuilder已经为你做了所有的工作,但是理解幕后发生了什么总是好的。

public static IWebHostBuilder CreateDefaultBuilder(string[] args)
{
    var builder = new WebHostBuilder()
        .UseKestrel()
        .UseContentRoot(Directory.GetCurrentDirectory())
        .ConfigureAppConfiguration((hostingContext, config) =>
        {
            var env = hostingContext.HostingEnvironment;
            config.AddJsonFile("appsettings.json", optional: true, reloadOnChange: true)
                  .AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional:true, reloadOnChange: true);
            if (env.IsDevelopment())
            {
                var appAssembly = Assembly.Load(new AssemblyName(env.ApplicationName));
                if (appAssembly != null)
                {
                    config.AddUserSecrets(appAssembly, optional: true);
                }
            }
            config.AddEnvironmentVariables();
            if (args != null)
            {
                config.AddCommandLine(args);
            }
        })
        .ConfigureLogging((hostingContext, logging) =>
        {
            logging.AddConfiguration(hostingContext.Configuration.GetSection("Logging"));
            logging.AddConsole();
            logging.AddDebug();
        })
        .UseIISIntegration()
        .UseDefaultServiceProvider((context, options) =>
        {
            options.ValidateScopes = context.HostingEnvironment.IsDevelopment();
        });
    return builder;
}

UserKestrel() 方法指定你的程序将使用Kestrel作为web server(基于libuv,跨平台),在web server上还有其他选项,如HttpSys(UseHttpSys),HttpSys只支持windows,但是有允许Windows身份认证的优点,可以安全的直接暴露在Internet上。而Kestrel需要放在像IIS,Nginx或Apache这些反向代理后面接收请求。

UseContentRoot()方法指定了应用程序的根目录,ASP.NET Core 能够找到整个站点的内容,比如config文件。需要注意的是这个根目录不是Web root(静态文件存放的地方),虽然默认的Web root 是基于内容根目录的([ContentRoot]/wwwroot)。

ConfigureAppConfiguration方法创建了configuration对象,应用程序在运行时通过它来读取配置。在CreateDefaultBuilder方法中,它将读取appsetting.json,appsetting.[environment].json,环境变量以及命令行参数中的配置,如果是开发环境,它还将使用user secrets。这个方法在ASP.NET Core 2.0 是新增的,我们将在后续讨论更多的细节。

ConfigureLogging 设置应用程序的日志。在CreateDefaultBuilder中,添加了控制台日志和debug日志。像ConfigureAppConfiguration一样,这个方法也是ASP.NET Core 2.0中新加的,我们将稍后讨论。

UseIISIntegration 配置了应用程序运行在IIS中。注意,UseKestrel依然是需要的。IIS作为反向代理而Kestrel作为宿主。如果app没有运行在IIS后,UseIISIntegration方法也不会产生负面影响,所以即使app运行在非IIS的场景下,这个调用也是安全的。

多数情况下,CreateDefaultBuilder提供的默认配置是足够的。如果需要默认配置以外的配置,可以在app的启动类中指定。调用UseStartup<T>方法时,T就是启动类。

如果CreateDefaultBuilder没有满足你的场景的需求,你可以自定义的创建IWebHost。如果你只需要小的改动,你可以修改调用CreateDefaultBuilder方法返回的WebHostBuilder(如,再次调用ConfigureAppConfiguration,添加更多的配置源)。如果你需要做大量的修改,你可以跳过CreateDefaultBuilder方法,像ASP.NET Core 1.0 或 1.1那样,构造你自己的WebHostBuilder。即使你这样做,你仍然可以使用ConfigureAppConfiguration和ConfigurLogging方法的优点。Web Host配置更多的细节可以在这里获取Web 主机

ASP.NET Core 环境

CreateDefaultBuilder的几个操作依赖于ASP.NET Core运行的环境,环境(Environment)在2.0中不是新的概念,但是还是值得简要的回顾一下,因为它会频繁的出现。

在ASP.NET Core中,app运行的环境被指定为ASPNETCORE_ENVIRONMENT变量。你可以设置任何你喜欢的值,但是Development,Staging和Production是典型的值。所以,在调用 dotnet run 命令之前设置了 ASPNETCORE_ENVIRONMENT变量的值为Development(或者,你在launchSettings.json文件中设置了environment变量值),你的app将运行在Development模式下(如果用Production代替了Development,默认将没有变量集)。有多个ASP.NET Core的功能通过使用这个变量来修改运行时的行为(比如Configuration和Logging),你也可以在你自己的代码中通过IHostingEnvironment服务来访问这个值。更多的信息可以在ASP.NET Core 文档中获取。

ASP.NET Core 配置

ASP.NET Core使用Microsoft.Extensions.Configuration包的IConfiguration接口提供运行时的配置。前面已提到,CreateDefaultBuilder将会从.json文件和环境变量中读取设置。配置系统是可扩展的,虽然它能够从各种提供者(json,xml,ini,environment...)中读取配置信息。

当使用IConfiguration和IConfigurationBuilder对象添加配置时,记住配置的顺序很重要。后面的配置的设置会覆盖前面的,所以你要先添加公共基础的配置,然后在添加特定环境的配置,这样后者才可能覆盖前者。

配置设置在ASP.NET Core 中是分层级的。当你创建新项目时,比如,appsetting.json包含了顶层的Logging元素,其下有子设置项。这些设置表示消息日志的最低优先级(LoggingLevel项),以及当消息来时是否在app的逻辑范围从而被记录(变量IncludeScopes)。要获取这些嵌套设置,你可以使用IConfiguration.GetSection()方法获取单个的配置节,或者指定特定配置的使用冒号做分隔符的全路径。所以,IncludeScopes在项目中的值可以这样获取:Configuration["Logging:IncludeScopes"]

//ASP.NET Core 的配置文件appsetting.json
{
  "Logging": {
    "IncludeScopes": false,
    "Debug": {
      "LogLevel": {
        "Default": "Warning"
      }
    },
    "Console": {
      "LogLevel": {
        "Default": "Warning"
      }
    }
  }
}

使用环境变量定义配置设置时,环境变量的名字需要包含所有层级,可以使用冒号(:)或双下划线(__)来分隔。比如,命名为Logging__IncludeScopes的环境变量将覆盖上面appsetting.json中IncludeScopes的设置,前提是,环境变量在appsetting后添加,CreateDefaultBuilder就是这样设计的,config.AddEnvironmentVariables();config.AddJsonFile()后添加。

因为 WebHost.CreateDefaultBuilder从appsetting.json和app.{environment}.json文件中读取配置,你会注意到当你改变环境时,日志的行为也改变了(appsettings.Development.json覆盖了appsettings.json中LogLevel的配置)。当设置了环境变量为Development,在调用dotnet run 后,你会注意到控制台记录了很多的日志(对调试有好处),而如果你设置了环境为Production,那么除了警告和错误信息,你看不到任何的日志(这样做是因为在生产环境下,应该尽可能少的记录日志)。

如果你有过ASP.NET Core 1.0和1.1的经验。你会注意到ConfigureAppConfiguration方法是2.0新增的。以前,常用的做法是在创建启动类时创建IConfiguration。使用ConfigureAppConfiguration方法来替代以前的做法是非常好的,因为他将IConfiguration对象存储在了app的DI容器中,以方便以后获取,以及在app的生命周期中提前了变量设置。

ASP.NET Core 日志

与装载配置一样,如果你熟悉以前的版本,你会记得日志装载也在Startup中,在2.0中,日志装载在IWebHost的ConfigureLogging方法中完成。

它仍然可以在Startup中装载(在Startup.ConfigureServices方法中使用services.Add-Logging),但是,在Web Host创建时配置日志,简化了Startup类,甚至可以在应用程序启动过程中更早地进行日志记录。

与配置一样,ASP.NET Core日志记录也可以扩展。注册不同的配置以记录到不同的终端。很多配置都及时有效的使用Microsoft.AspNetCore.All库。

从WebHost.CreateDefaultBuilder的源代码可以看出,在ILoggingBuilder上调用指定的提供者扩展方法可以添加日志提供者(如AddDebug或AddConsole)。如果你使用了WebHost.CreateDefaultBuilder,但是还想注册其他的日志提供者,可以在CreateDefaultBuilder返回的IWebHostBuilder上调用ConfigureLogging方法。

一旦提供者已被注册和日志已被配置。ASP.NET Core会自动记录传入的请求消息,你也可以从DI容器中获取ILogger对象,调用ILogger.Log()方法,记录自己的消息。

启动类型

现在你看到了Program.cs 是怎样创建Web host的,现在让我们跳到Startup.cs文件中。app将要使用的startup,是在创建IWebHost时调用StartUp方法是指定的。ASP.NET Core 2.0中Startup.cs没有太多的改变(除了将logging和configuration移动到Program.cs文件中实现),但是我将简要的回顾一下Startup类中两个重要的方法,因为它们对ASP.NET Core应用程序非常重要。

Startup类的ConfigureServices方法通过依赖注入将服务添加到app的DI容器中,所有的ASP.NET Core的应用都有默认的DI容器去存储服务,方面后面使用。DI容器使得服务变得可用,你已经看过了一对例子,ConfigureAppConfiguration和ConfigureLogging将在你的app中添加服务到容器,以方便后续使用。在运行时,如果一个这样的类型的实例被调用,ASP.NET Core 将自动从DI容器中获取对应的对象。

举个例,ASP.NET Core 2.0 程序的Startup类有一个构造函数,他有一个IConfiguration参数,当IWebHost开始运行时,这个构造函数将会自动被调用。这时,ASP.NET Core 将会从DI容器中提供IConfiguration参数给构造函数。

另一个例子,如果你想从Razor页面记录消息,你可以在页面模型的构造函数中将logger对象作为参数向App请求(像Startup请求IConfiguration对象一样),或者在cshtml中使用@inject语法,如下:

@using Microsoft.Extensions.Logging
@inject ILogger<Index_Page> logger

@functions {
  public void OnGet()
    {
      logger.LogInformation("Beginning GET");
    }
}

同样可以来获取IConfiguration对象或任何其他已注册的类型。用DI容器这种方法,可使Startup类,Razor页面,controller等与所依赖的服务解耦。

在这节开始提到,在Startup.ConfigureServices方法中,服务被添加到DI容器。你使用程序模块创建app时,已经有一个服务在ConfigureServices方法中被注册了:services.AddMvc。就像你猜的一样,这个注册的服务需要MVC框架。

另一个常见的服务是Entity Framework Core,尽管他不会再本例中使用,使用Entity Framework Core 的应用程序通常使用services.AddDbContext()方法,注册所需的DBContexts,以保证他们使用Entity Framework模型。

More details on dependency injection in ASP.NET Core are available at bit.ly/2w7XtJI.
你也可以使用services.AddTransient(),services.AddScoped() 或 services.Add­Singleton()方法注册你自己的类型和服务。注册singleton将在每次请求时返回一个单列,注册一个transient将返回在每次请求的时候返回一个新的实例。注册scoped将在一个独立的http请求线程中返回一个单列,有关ASP.NET Core中依赖项注入的更多详细信息,请参见这里

http请求--处理管道和中间件

Startup类中另一个重要的方法是Configure。这是ASP.NET Core应用程序的心脏,http请求管道在这里被装载。在这个方法中不同的中间件被注册,这些中间件将处理http请求,并生成响应。

在Startup.Configure方法中,中间件被添加到IApplicationBuilder中形成处理管道。当请求进入时,第一注册的中间件被调用,这个中间件将执行它的逻辑,然后调用下一个中间件,如果它完成了它的处理逻辑,将返回上一个中间件。在图4中示出了在请求到达之后按顺序调用中间件组件的模式,然后在处理之后以相反的顺序调用中间件组件。

图4 ASP.NET Core 中间件处理管道

举一个具体的例子,图5显示了模板工程的Configure方法,当新请求进来时,它将首先访问DeveloperExceptionPage中间件或ExceptionHandler中间件,这取决你的ASPNETCORE_ENVIRONMENT的环境变量。这些中间件最初不会做过多的操作,但是在后续的中间调用并退出中间件管道后,他们将关注异常的处理。

//图5 ASP.NET Core Startup.Configure 方法装载中间件管道
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
  if (env.IsDevelopment())
  {
    app.UseDeveloperExceptionPage();
  }
  else
  {
    app.UseExceptionHandler("/Error");
  }

  app.UseStaticFiles();

  app.UseMvc(routes =>
  {
    routes.MapRoute(
      name: "default",
      template: "{controller=Home}/{action=Index}/{id?}");
  });
}

接下来,StaticFiles中间件将会被调用,它可以为请求提供静态文件(图片或样式等)。如果他执行了,他将停止管道并返回上一个中间件(异常处理中间件)。如果StaticFiles中间件不能提供响应,他将调用下一个中间件--MVC中间件,MVC中间件会尝试路由这个请求到MVC控制器(或者Razor页面)。

中间件组件的注册顺序非常重要。如果UseStaticFiles位于UseMvc之后,应用程序将尝试在检查静态文件之前将所有请求路由到MVC控制器,这可能会导致明显的性能下降。如果异常处理中间件在管道后面出现,它将无法处理先前中间件组件中发生的异常。

Razor页面

除了.csproj,program.cs和startup.cs文件之外,你的ASP.NET Core项目还包含一个Pages文件夹,其中包含应用程序的Razor页面。Razor页面类似于MVC视图,但是请求可以直接路由到Razor页面而无需单独的控制器。这样可以简化基于页面的应用程序并将视图和视图模型保持在一起。支持页面的模型可以直接包含在cshtml页面中,或者在单独的代码文件中,它与@model指令一起引用。要了解Razor Pages的更多信息,可以查看Steve Smith的文章“Simpler ASP.NET MVC Apps with Razor Pages”

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 206,126评论 6 481
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 88,254评论 2 382
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 152,445评论 0 341
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 55,185评论 1 278
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 64,178评论 5 371
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,970评论 1 284
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,276评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,927评论 0 259
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,400评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,883评论 2 323
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,997评论 1 333
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,646评论 4 322
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,213评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,204评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,423评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,423评论 2 352
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,722评论 2 345

推荐阅读更多精彩内容