ASP.NET 5

深入探究 ASP.NET 5 运行时

Daniel Roth

去年 11 月,Microsoft 宣布推出 ASP.NET 5 这一新的跨平台开源框架,以支持使用 Microsoft.NET Framework 构建现代 Web 和云应用程序。我们(我所在的 ASP.NET 开发团队)在纽约举办的 Connect(); 活动上发布了 ASP.NET 5 预览版与 Visual Studio 2015 预览版。在 2014 年 12 月 15 日出版的《MSDN 杂志》特刊 (bit.ly/1K4PY4U) 中,我在“ASP.NET 5 预览版简介”一文中向您介绍了 ASP.NET 5 运行时及其新的 Visual Studio 项目系统。自 12 月最后一次提及 ASP.NET 5 以来,ASP.NET 5 又推出了两个预览版:Visual Studio 2015 CTP 5 Beta2 和 Visual Studio 2015 CTP 6 Beta3。每个版本中,该框架都继续发展和改进。在许多情况下,这些改进是由 .NET 社区通过 GitHub (bit.ly/1DaY7Cd) 上的公共 ASP.NET 项目慷慨贡献的。在本文中,我将深入探究新的 ASP.NET 5 运行时,以便您了解最新版本中的更改内容。

K 运行时环境 (KRE)

正如您在去年 12 月所见,ASP.NET 5 基于可以托管多个 .NET CLR 之一的灵活、跨平台运行时主机。您可以在具有其完整 API 集以实现最大兼容性的 .NET Framework 上运行 ASP.NET 5 应用程序。您也可以在新的 .NET Core 上运行 ASP.NET 5,从而实现真正的端到端部署,这样您可以将其复制到现有环境中,而无需在计算机上进行任何其他更改。将来,您还能够在 .NET Core 上跨平台运行 ASP.NET 5,而且现在有针对 Mono 上的跨平台运行的社区支持。

在我们最终确定正式名称之前,ASP.NET 5 的运行时托管基础结构目前称为 K 运行时环境 (KRE),这是一个通用的占位符名称。KRE 提供的环境包含了运行 .NET 应用程序所需的所有内容:主机进程、CLR 托管逻辑、托管入口点发现等等。构建 KRE 以运行跨平台 .NET Web 应用程序,但它也可以运行其他类型的 .NET 应用程序,如控制台应用。KRE 基于的 .NET CLR 和基类库与 .NET 开发人员逐渐熟悉和喜爱的相同,同时支持在 Windows、OS X 和 Linux 上跨平台运行 .NET 应用程序。

在逻辑上,KRE 具有五个功能层。我将介绍其中每一层及其职责。

第 1 层。本机进程:本机进程是一个非常薄的层,负责查找和调用本机 CLR 主机,将给定参数传递至将由堆栈其余部分使用的进程。在 Windows 上,这是一个本机可执行文件 (klr.exe);在 Mac 或 Linux 上,它是一个可执行 bash 脚本。在 IIS 上运行通过新的本机 HTTP 模块或使用 Helios 加载程序来实现。Helios 加载程序利用 .NET Framework 4.5.1 中的可扩展性挂钩启动 KRE,而无需安装新的 IIS 模块。通过本机 HTTP 模块,您可以在 IIS 上运行基于 .NET Core 的 Web 应用程序,无需依赖 .NET Framework。尽管本机 HTTP 模块尚未公开推出,但我们计划在将来的预览版中提供。

第 2 层和第 3 层。本机 CLR 主机和 CLR:本机 CLR 主机具有三个主要职责:

  1. 引导 CLR。实现方式取决于 CLR 的版本。例如,引导 .NET Core 涉及加载 coreclr.dll、配置和启动运行时,以及创建所有托管代码都在其中运行的 AppDomain。对于 Mono 和 .NET Framework 4.5.1,该进程在某种程度上各不相同,但结果是相同的。
  2. 调用托管入口点,这是下一层。
  3. 在本地主机的入口点返回时,此进程将清理并关闭 CLR — 也就是说,卸载应用程序域并停止运行时。

第 4 层:托管入口点:这一层是以托管代码编写的第一层。它负责:

  1. 加载程序集并满足 lib 文件夹中的依赖关系。
  2. 设置 IApplicationEnvironment 和核心依赖关系注入基础结构。
  3. 调用指定应用程序或应用程序主机的主入口点。

在此层,程序集仅从应用程序基路径或指定的 lib 文件夹加载。它是添加其他加载程序以解析 NuGet 包或甚至在运行时编译的代码中的依赖关系的下一层。

第 5 层:应用程序主机/应用程序:如果开发人员将整个应用程序编译到 lib 文件夹中磁盘上的程序集,则这一层是应用程序 — 最终用户看到的应用程序。如果想要执行此操作,则可在启动第 1 层时编译您的应用程序并传递包含标准 Main 入口点的 DLL 名称。

但是,在大多数情况下,您最好使用应用程序主机来帮助解析应用程序依赖关系并运行您的应用程序。Microsoft.Framework.ApplicationHost 是 KRE 中提供的应用程序主机,其职责包括:

  1. 遍历 project.json 中的依赖关系并构建应用程序将使用的依赖关系闭包。依赖关系遍历逻辑在 bit.ly/1y5lZEm 中有详细介绍。
  2. 添加可以从各种源(如安装的 NuGet 包、使用 Roslyn 在运行时编译的源等)加载程序集的其他程序集加载程序。
  3. 调用程序集入口点,该程序集的名称在本机进程启动时作为参数提供。该程序集可以是 ApplicationHost 知晓如何加载入口点的任何程序集。KRE 附带的 ApplicationHost 知晓如何查找 public void Main 方法。这是用于设置 ASP.NET 托管层的入口点,它知晓如何为 Web 应用程序查找 Startup.cs 并运行 Configure 方法。

了解 KRE 时有一点需要注意,即这是堆栈的低级部分。当运行在 KRE 级别时,需要面对的问题大部分仍是关于查找和加载动态链接库 (Dll)。KRE 包含的逻辑允许您在应用程序级别只考虑包和其他顶级依赖关系。

跨平台 SDK 工具

KRE 附带了一个 SDK,其中包含构建跨平台 .NET 应用程序所需的全部内容。在上一篇有关 ASP.NET 5 的文章中,我介绍了如何使用 KRE 版本管理器 (KVM) 工具列出计算机上已安装的 KRE、安装新的 KRE 以及选择想要使用的 KRE。您可以在 bit.ly/1y5mqyi 找到有关如何为 OS 安装 KVM 的说明。

KVM 从使用 KRE_FEED 环境变量配置的 NuGet 源安装 KRE。KRE 不是传统意义上的 NuGet 包,因为它们不是您曾经依赖的包;NuGet 只是一个分发 KRE 并分配 KRE 版本的便捷方法。默认情况下,KRE 通过将 KRE .zip 文件复制并解压缩到 %USERPROFILE%\.k\runtimes 来安装 KRE。

我先前也介绍了用于安装、还原和创建 NuGet 包的 K 程序包管理器 (KPM) 工具。我们计划将 KPM 工具重命名为 nuget,并使其与现有的 NuGet 客户端保持一致。作为此调整的一部分,一些 kpm 子命令已进行了重命名。现在,您可以使用捆绑命令将用于发布的应用程序和用于为项目生成和创建 NuGet 包的 pack 命令捆绑在一起。更新后的 build 命令将在不打包任何内容的情况下生成原始生成输出。此外,还有新的 wrap 命令,它允许工具引用 project.json 中现有的基于 csproj 的项目。默认情况下,包现在安装在 %USERPROFILE%\.k\packages 文件夹中,但您可以通过在 global.json 文件中设置包路径对此进行控制。

跨平台 .NET 控制台应用

我现在将向您介绍如何使用 KRE 创建一个简单的跨平台 .NET 控制台应用。首先,我需要创建一个带有入口点的 DLL,并且我可以使用 Visual Studio 2015 中的 ASP.NET 5 控制台应用项目模板实现此目的。该代码应如下所示:

public class Program
{
  public void Main(string[] args)
  {
    Console.WriteLine("Hello World");
    Console.ReadLine();
  }
}

这看起来很熟悉,但请注意入口点实际上是实例方法。除了静态的 Program.Main 入口点,KRE 还支持基于实例的入口点。您甚至可以使主入口点异步进行并返回一个任务。通过使主入口点成为实例方法,您可以通过运行时环境将服务注入您的应用程序。

您可以从 Visual Studio 内运行该应用程序,或者在命令行上通过从包含应用程序的 project.json 文件的目录运行“k run”来运行。K 命令实际上只是一个简单的批处理文件,而“k run”命令可扩展至以下内容:

klr.exe --appbase . Microsoft.Framework.ApplicationHost run

K 命令执行本机进程 (klr.exe)、指定应用程序基为当前目录,然后指定默认应用程序主机。本机进程没有关于默认应用程序主机的专门知识 — 它只是寻找 Microsoft.Framework.ApplicationHost 程序集中的标准入口点并进行调用。

如果不想使用默认应用程序主机,您可以调用本机层以直接调用您的应用程序。若要执行此操作,首先构建项目为控制台应用生成 DLL。请确保在项目的“构建”属性中选中“在构建上生成输出”选项。您可以在解决方案文件夹中的项目目录下找到生成输出。导航到包含适用于您所使用 KRE 类型的内置 DLL 的目录,并调用“klr.exe <DLL 名称>”查看来自控制台应用的输出。

对于编写应用程序而言,直接调用 DLL 是一种级别非常低的原始方法。您不使用默认应用程序主机,因此您放弃了 project.json 支持和改善的基于 NuGet 的依赖关系管理支持。而您所依赖的所有库可从指定的 lib 文件夹轻松加载。在本文的剩余部分中,我将使用默认的应用程序主机。

您可以使用 IServiceManifest 服务枚举运行时环境提供的所有服务。根本不必指定任何额外的依赖关系来使用该程序集,因为它是非特定程序集接口 (ANI)。ANI 是由仅通过其名称和命名空间标识的类型,它们是 KRE 的一个功能。不同的程序集中具有相同名称和命名空间的两个非特定程序集类型被视为是等效的。这意味着常见抽象只需本地声明即可,而不必声明常见组件上的依赖关系。

您可以在您的控制台应用中定义自己的非特定程序集 IServiceManifest 接口,如下所示:

namespace Microsoft.Framework.DependencyInjection.ServiceLookup
{
  [AssemblyNeutral]
  public interface IServiceManifest
  {
    IEnumerable<Type> Services { get; }
  }
}

但是,AssemblyNeutralAttribute 来自何处呢?它当然与程序集无关!因此,您也可对其进行本地声明:

namespace Microsoft.Net.Runtime
{
  [AssemblyNeutral]
  [AttributeUsage(AttributeTargets.All, 
    Inherited = false, AllowMultiple = true)]
  public sealed class AssemblyNeutralAttribute : Attribute
  {
  }
}

这些非特定程序集类型将在运行时与 KRE 正在使用的相同类型进行统一。

图 1 中的代码将 IServiceManifest 服务注入应用程序,方式是:将一个构造函数添加到 Program 类,然后循环访问服务。

图 1 将 IServiceManifest 服务注入应用程序

namespace ConsoleApp1
{
  public class Program
  {
    public Program(IServiceManifest serviceManifest)
    {
      ServiceManifest = serviceManifest;
    }
    IServiceManifest ServiceManifest { get; }
    public void Main(string[] args)
    {
      foreach (Type type in ServiceManifest.Services)
      {
        Console.WriteLine(type.FullName);
      }
    }
  }
}

应用程序主机将启用对 project.json 的支持、添加其他程序集解析程序(用于处理 NuGet 包、项目引用等)以及在调用应用程序入口点之前向应用程序提供多项其他服务。当运行控制台应用时,输出应如下所示:

Microsoft.Framework.Runtime.IAssemblyLoaderContainer
Microsoft.Framework.Runtime.IAssemblyLoadContextAccessor
Microsoft.Framework.Runtime.IApplicationEnvironment
Microsoft.Framework.Runtime.IFileMonitor
Microsoft.Framework.Runtime.IFileWatcher
Microsoft.Framework.Runtime.ILibraryManager
Microsoft.Framework.Runtime.ICompilerOptionsProvider
Microsoft.Framework.Runtime.IApplicationShutdown

有各种服务用于执行文件监视、遍历应用程序中“库”(项目、包、程序集)的结构、获取正在使用的编译选项以及关闭应用程序。由于 ASP.NET 5 和 KRE 仍处于预览版和开发过程中,因此您看到的确切服务列表可能会不同。

托管

ASP.NET 托管层运行在 KRE 之上,并负责查找 Web 服务器以在其上运行、查找应用程序的启动逻辑、在服务器上托管应用程序,然后在应用程序关闭时进行清理。它还向应用程序提供大量其他与托管相关的服务。

ASP.NET 托管 DLL(Microsoft.AspNet.Hosting,位于 bit.ly/­1uB6ulW)具有由 KRE 应用程序主机调用的入口点方法。通过指定 --server 命令行选项或配置数据的其他源(如 config.json 或环境变量),您可以配置您想要使用的 Web 服务器。然后,托管层将加载指定的服务器程序集/类型,以查找可用于初始化和启动服务器的 IServerFactory。通常情况下,服务器必须在 project.json 中您的依赖关系中列出,以便可以加载它。

请注意,在 project.json 中定义的命令实际上只是向 klr.exe 附加的多组命令行参数。例如,默认的 ASP.NET Starter Web App 项目模板在 project.json 中包含大量命令,类似如下所示:

"commands": {
  /* Change the port number when you are self hosting this application */
  "web": "Microsoft.AspNet.Hosting 
    --server Microsoft.AspNet.Server.WebListener
    --server.urls http://localhost:5000",
  "gen": "Microsoft.Framework.CodeGeneration",
  "ef":  "EntityFramework.Commands"
},

因此,如果您打算运行“k web”,您真正运行的是:

klr.exe --appbase . Microsoft.Framework.ApplicationHost Microsoft.AspNet.Hosting
  --server Microsoft.AspNet.Server.WebListener --server.urls http://localhost:5000

启动

ASP.NET 托管层也负责查找您的应用程序的启动逻辑。通常情况下,您的应用程序启动逻辑在 Startup 类中定义,其中 Configure 方法用于设置请求管道,ConfigureServices 方法用于配置您的应用程序所需的任何服务,如图 2 中所示。

图 2 Startup 类

namespace WebApplication1
{
  public class Startup
  {
    public void ConfigureService(IServiceCollection services)
    {
      // Add services for your application here
    }
    public void Configure(IApplicationBuilder app)
    {
      // Configure your application pipeline here
    }
  }
}

在您的 Configure 方法中,使用 IApplicationBuilder 接口可为应用程序构建请求管道。应用程序生成器使您能够“使用”中间件、创建“新的”应用程序生成器并“构建”请求委托。请求委托是 ASP.NET 5 中的核心运行时构造。请求委托采用 HttpContext,并以异步方式通过其执行一些有用操作:

public delegate Task RequestDelegate(HttpContext context);

ASP.NET 5 中间件将管道中的下一个请求委托作为输入并向请求委托提供中间件逻辑。返回的请求委托可能调用也可能不调用管道中的下一个请求委托。作为运行不调用下一个请求委托的中间件逻辑的快捷方式,您可以在 IApplicationBuilder 上使用 Run 扩展方法:

app.Run(async context => await context.Response.WriteAsync("Hello, world!"));

这与编写下面这个其中将直接忽略下一个参数的内联中间件相同:

app.Use(next => async context =>
  await context.Response.WriteAsync("Hello, world!"));

若要创建可重用的中间件,您可以将其编写为类,其中,按照惯例,下一个请求委托将注入构造函数以及中间件需要的任何其他服务或参数。随后,将中间件的请求委托实施为异步调用方法,如图 3 中所示。

图 3 将中间件实施为可重用类

using Microsoft.AspNet.Builder;
using Microsoft.AspNet.Http;
using System.Threading.Tasks;
public class XHttpHeaderOverrideMiddleware
{
  private readonly RequestDelegate _next;
  public XHttpHeaderOverrideMiddleware(RequestDelegate next)
  {
    _next = next;
  }
  public Task Invoke(HttpContext httpContext)
  {
    var headerValue =
      httpContext.Request.Headers["X-HTTP-Method-Override"];
    var queryValue =
      httpContext.Request.Query["X-HTTP-Method-Override"];
    if (!string.IsNullOrEmpty(headerValue))
    {
      httpContext.Request.Method = headerValue;
    }
    else if (!string.IsNullOrEmpty(queryValue))
    {
      httpContext.Request.Method = queryValue;
    }
    return _next.Invoke(httpContext);
  }
}

您可以按照此惯例使用中间件,以在 IApplicationBuilder 上使用 UseMiddleware <T> 扩展方法。在下一个请求委托和任何注入的服务之后,您传入此方法的任何其他选项都将注入此中间件构造函数。按照惯例,中间件还应在 IApplicationBuilder 上定义其自己特定的 Use 扩展方法,如下所示:

public static class BuilderExtensions
{
  public static IApplicationBuilder UseXHttpHeaderOverride(
    this IApplicationBuilder builder)
  {
    return builder.UseMiddleware<XHttpHeaderOverrideMiddleware>();
  }
}

然后,将中间件添加到应用程序管道,如下所示:

public class Startup
{
  public void Configure(IApplicationBuilder app)
  {
    app.UseXHttpHeaderOverride();
  }
}

ASP.NET 5 附带了一组丰富的预构建中间件。其中包含用于处理静态文件、路由、错误处理、诊断和安全的中间件。您可以在 Microsoft 和社区查找、下载并安装中间件作为 NuGet 包 (nuget.org)。

配置服务

像您在此堆栈中所看到的所有其他入口点一样,Startup 类可以将服务从其构造函数的托管层注入或作为 Configure 和 ConfigureServices 方法上的其他参数注入。如果枚举在启动时可用的服务(使用 IServiceManifest),您会看到现在甚至有更多服务可用:

Microsoft.Framework.Runtime.IAssemblyLoaderContainer
Microsoft.Framework.Runtime.IAssemblyLoadContextAccessor
Microsoft.Framework.Runtime.IApplicationEnvironment
Microsoft.Framework.Runtime.IFileMonitor
Microsoft.Framework.Runtime.IFileWatcher
Microsoft.Framework.Runtime.ILibraryManager
Microsoft.Framework.Runtime.ICompilerOptionsProvider
Microsoft.Framework.Runtime.IApplicationShutdown
Microsoft.AspNet.Hosting.IHostingEnvironment
Microsoft.Framework.Runtime.IProjectResolver
Microsoft.Framework.Logging.ILoggerFactory
Microsoft.Framework.Runtime.ICache
Microsoft.Framework.Runtime.ICacheContextAccessor
Microsoft.Framework.DependencyInjection.ITypeActivator
Microsoft.Framework.Runtime.IAssemblyLoadContextFactory

IHostingEnvironment 服务可以让您访问应用程序的 Web 根路径(通常为 www 文件夹)以及 Web 根的 IFileProvider 抽象。IProject­Resolver 可用于查找解决方案中的其他项目。有日志记录和缓存服务以及可感知依赖关系注入的类型激活器。IAssemblyLoadContextFactory 为您提供创建新程序集加载上下文的抽象。

您可配置现有的服务,并在 Startup 类内的 ConfigureServices 方法中为您的应用程序添加新的服务。ConfigureServices 方法采用 IServiceCollection,您可以在其中添加额外服务或修改现有服务。ASP.NET 5 中附带一个简单的内置 IoC 容器,该容器在托管入口点层进行设置以启动系统,但您可以使用自选容器轻松地替换该内置容器。

框架通常会提供 Add 扩展方法,以将它们的服务添加到 IServiceCollection。例如,下面说明如何添加由 ASP.NET MVC 6 使用的服务:

public void ConfigureServices(IServiceCollection services)
{
  // Add MVC services to the services container
  services.AddMvc();
}

当您将服务添加到服务集合时,它们可以是以下三种类型之一:暂时、作用域或单一实例。暂时服务是每次从容器进行请求时创建的。作用域服务仅在它们并不存在于当前作用域中时创建。对于 Web 应用程序而言,会针对每个请求创建容器作用域,以便您可以根据请求考虑作用域服务。单一实例服务只会创建一次。

Web.config 和 System.Configuration 样式的 app.config 文件在 ASP.NET 5 中不受支持。而 ASP.NET 5 附带一个经过简化的新配置 API(Microsoft.Framework.ConfigurationModel,位于 bit.ly/1yxC7gA),用于处理配置数据。新的配置 API 允许您检索和操作来自各种源的配置数据。对于 JSON、XML、INI、命令行参数和环境变量,还包括了默认配置提供程序。您可以指定多个提供程序并按其添加顺序进行使用。通过支持基于环境的配置,可以更轻松地将应用程序部署到环境,并为其选取适用于该环境的设置。

若要初始化配置数据,请将所需配置提供程序添加到新的配置实例:

public Startup(IHostingEnvironment env)
{
  Configuration = new Configuration()
    .AddJsonFile("config.json")
    .AddEnvironmentVariables();
}

然后,您可以按名称请求配置值:

string user = Configuration.Get("user");
string password = Configuration.Get("password");

您可以使用选项模型通过强类型化的方式使配置数据在整个应用程序中可用。这些选项只是 Plain Old C# Object (POCO) 类,其拥有大量用于配置的属性。通过调用 AddOptions 可在应用程序中提供这些选项,这会将 IOptions<TOption> 服务添加到容器。只要支持依赖关系注入,该服务就可用于访问不同类型的选项。

若要配置选项,可将 IConfigureOptions<TOptions> 服务添加到容器。IOptions<TOption> 服务的默认实施将收集所有 IConfigure­Options<TOption> 服务,并在向使用者提供选项实例之前使用它们来配置该选项实例。作为添加选项配置逻辑的快捷方式,您可以使用 Configure<TOption> 方法,如下所示:

services.Configure<MvcOptions>(options => options.Filters.Add(
  new MyGlobalFilter()));

选项模型和配置模型可以配合使用。您可以轻松地将配置数据绑定到选项,通过使用采用 Configuration 实例的 Configure 重载按属性名进行匹配,如下所示:

services.Configure<MyOptions>(Configuration);

请求处理

启动 Web 服务器后,其将开始侦听请求并为每个请求调用应用程序管道。服务器将每个请求展现为各个公开一组精细功能接口的服务器环境对象。这些功能接口用于发送文件、Web 套接字、会话支持、客户端证书等。您可以在 GitHub 上查看受支持功能接口的完整列表 (bit.ly/1Dh7eBC)。例如,以下是用于 HTTP 请求的功能接口:

[AssemblyNeutral]
public interface IHttpRequestFeature
{
  string Protocol { get; set; }
  string Scheme { get; set; }
  string Method { get; set; }
  string PathBase { get; set; }
  string Path { get; set; }
  string QueryString { get; set; }
  IDictionary<string, string[]> Headers { get; set; }
  Stream Body { get; set; }
}

通常,您无需针对各种功能接口直接进行编码。Web 服务器使用这些功能接口向主机层公开低级功能。托管层将这些功能接口封装到强类型化 HttpContext 中,然后传递至应用程序。Web 服务器、托管层和应用程序之间的松散耦合级别允许实施并重用服务器(而无需受限于一个特定的托管模型),并且其允许在不同服务器上托管应用程序。ASP.NET 5 附带了内置支持,支持在 IIS 上 (Microsoft.AspNet.Server.IIS) 运行、直接在精简 HTTP.SYS 包装 (Microsoft.AspNet.Server.Web­Listener) 上运行以及在称为 Kestrel (Microsoft.AspNet.Server.Kestrel) 的新 .NET 跨平台 Web 服务器上运行。

Open Web Interface for .NET (OWIN) 社区标准 (owin.org) 就 Web 应用程序的松散耦合有着类似的共同目标。OWIN 标准化了 .NET 服务器和应用程序之间的对话方式。ASP.NET 5 通过 Microsoft.AspNet.Owin 包 (bit.ly/15IQwA5) 支持 OWIN。您可以在基于 OWIN 的 Web 服务器上托管 ASP.NET 5 (bit.ly/1DaU4FZ),并且您可以在 ASP.NET 5 管道中使用 OWIN 中间件 (bit.ly/1EqmoIB)。

Katana 项目 (katanaproject.codeplex.com) 是 Microsoft 实现对 OWIN 的支持做出的初步努力,ASP.NET 5 中的许多想法和理念就是从中衍生而来。Katana 对于中间件管道构建和在多个服务器上进行托管有着类似模型。但是,与直接向开发人员公开低级别 OWIN 抽象的 Katana 不同,ASP.NET 5 改为从 OWIN 桥接到更加方便且用户友好的抽象。只需少量额外代码,您仍然可以通过 OWIN 桥在 ASP.NET 5 中使用 Katana 中间件(请参阅 bit.ly/1BpaXe2 中的操作说明示例)。

总结

ASP.NET 5 运行时从一开始就支持跨平台 Web 应用程序。ASP.NET 5 有一个灵活的分层体系结构,可以运行在 .NET Framework、.NET Core 上或在 Mono 上跨平台运行(未来也可在 .NET Core 上运行!)。新的 ASP.NET 5 托管模型可以让您轻松编写应用程序并将其托管在所选的 Web 服务器上。整个运行时支持依赖关系注入并易于配置。我希望您能乐于了解这一新的 ASP.NET 5 运行时!欢迎您通过我们的 GitHub 项目 (github.com/aspnet/home) 提供反馈和稿件。


Daniel Roth 是目前正专注于 ASP.NET 5 的 ASP.NET 团队的资深项目经理。他热衷于 .NET 开发,致力于让框架变得简单易用以使客户更加满意。

衷心感谢以下 Microsoft 技术专家对本文的审阅:Glenn Condron、David Fowler、Murat Girgin 和 Chris Ross