ASP.NET

本页使用了标题或全文手工转换
维基百科,自由的百科全书
ASP.NET
开发者Microsoft
首次发布2002年1月5日,​22年前​(2002-01-05
当前版本
  • 3.3.0 (2023年10月24日;稳定版本)[1]
编辑维基数据链接
源代码库 编辑维基数据链接
编程语言.NET 编程语言,例如C#VB.NET
类型Web应用程序
许可协议Apache 2.0
网站www.asp.net
ASP.NET
扩展名
.aspx、.cshtml、.vbhtml
互联网媒体类型
text/html
开发者Microsoft

ASP.NET是由微软.NET Framework框架中所提供,开发Web应用程序的类别库,封装在System.Web.dll文件中,显露出System.Web命名空间,并提供ASP.NET网页处理、扩展以及HTTP通道的应用程序与通信处理等工作,以及Web Service的基础架构。ASP.NET是ASP技术的后继者,但它的发展性要比ASP技术要强大许多。

ASP.NET可以运行在安装了.NET Framework的IIS服务器上,若要在非微软的平台上执行,则需要使用Mono平台[2],ASP.NET在2.0版本已经定型,在.NET Framework 3.5上则加上了许多功能,像是ASP.NET AJAXASP.NET MVC FrameworkASP.NET Dynamic DataMicrosoft Silverlight的服务器控件等。

很多人都把 ASP.NET 当做是一种编程语言,但它实际上只是一个由 .NET Framework 提供的一种开发平台 (development platform),并非编程语言。也可认为ASP.NET是.NET组件,任何.NET语言,例如C#,可以引用该组件,创建网页或Web服务。

为了因应云端化所诱发的多作业平台集成与开发能力,微软特别开发一个新一代的 ASP.NET,称为 ASP.NET vNext,并于 2014 年命名为 ASP.NET 5,但随后于 2016 年将它更名为 ASP.NET Core,由于架构上的差异颇大,因此未来 ASP.NET 与 ASP.NET Core 将是分别发展与维护,Windows 平台的 ASP.NET 4.6 以上版本仍维持 Windows Only,但 ASP.NET Core 则是具有跨平台 (Windows, Mac OSX 与 Linux) 的能力。

发展缘起

斯科特·格思里英语Scott Guthrie

ASP.NET的前身ASP技术,是在IIS 2.0上首次推出(Windows NT 3.51),当时与 ADO 1.0 一起推出,在IIS 3.0(Windows NT 4.0)发扬光大,成为服务端应用程序的热门开发工具,微软还特别为它量身打造了Visual InterDev开发工具,在1994年到2000年之间,ASP技术已经成为微软推展Windows NT 4.0平台的关键技术之一,数以万计的ASP网站也是这个时候开始如雨后春笋般的出现在网络上。由于它的简单以及高度客制化的能力,也是它能迅速窜起的原因之一。

不过ASP的缺点也逐渐的浮现出来:

  • 意大利面型的程序开发方法,让维护的难度提高很多,尤其是大型的ASP应用程序。
  • 直译式的VBScriptJScript语言,让性能有些许的受限。
  • 延展性因为其基础架构扩展性不足而受限,虽然有COM组件可用,但开发一些特殊功能(像文件上传)时,没有来自内置的支持,需要寻求第三方软件商开发的组件。

1997年时,微软开始针对ASP的缺点(尤其是意大利面型的程序开发方法)准备开始一个新项目来开发,当时ASP.NET的主要领导人斯科特·格思里英语Scott Guthrie刚从杜克大学毕业,他和IIS团队的Mark Anders经理一起合作两个月,开发出了下一代ASP技术的原型,这个原型在1997年的圣诞节时被发展出来,并给予一个名称:XSP[3],这个原型产品使用的是Java语言。不过它马上就被纳入当时还在开发中的CLR平台,Scott Guthrie事后也认为将这个技术移植到当时的CLR平台,确实有很大的风险(huge risk),但当时的XSP团队却是以CLR开发应用的第一个团队。

为了将XSP移植到CLR中,XSP团队将XSP的核心程序全部以C#语言重新撰写(在内部的项目代号是 "Project Cool",但是当时对公开场合是保密的),并且改名为ASP+,作为ASP技术的后继者,并且也会提供一个简单的移转方法给ASP开发人员。ASP+首次的Beta版本以及应用在PDC 2000中亮相,由Bill Gates发表Keynote(即关键技术的概览),由富士通公司展示使用COBOL语言撰写ASP+应用程序,并且宣布它可以使用Visual Basic.NETC#PerlPython语言(后两者由ActiveState公司开发的互通工具支持)来开发。

在2000年第二季时,微软正式推动.NET策略,ASP+也顺理成章的改名为ASP.NET,经过四年的开发,第一个版本的ASP.NET在2002年1月5日亮相(和.NET Framework 1.0),Scott Guthrie也成为ASP.NET的产品经理(到现在已经开发了数个微软产品,像ASP.NET AJAXMicrosoft Silverlight)。

ASP.NET处理架构

ASP.NET 运行的架构分为几个阶段:

  • 在 IIS 与 Web 服务器中的消息流动阶段。
  • 在 ASP.NET 网页中的消息分派。
  • 在 ASP.NET 网页中的消息处理。

Web服务器的消息流动阶段

ASP.NET执行架构

当装载(hosting) ASP.NET 的 Web 服务器接收到 HTTP 请求时,HTTP 聆听程序 (HTTP Listener,实际上是内核态的HTTP.SYS) 会将请求转交给 URL 指定的网站应用程序池中的工作者进程 (Worker Process)[4],ASP.NET 的工作者进程接收到这个请求之后,装载专用于处理ASP.NET页面的一个ISAPI扩展“aspnet_isapi.dll”,并将HTTP请求传给它。若是 IIS 5.0 时则是 aspnet_wp.exe。

aspnet_isapi.dll负责加载ASP.NET应用程序的运行环境CLR(IIS 7集成模式下,CLR是预加载的),每个ASP.NET应用程序都运行于自己的应用程序域(AppDomain)中。每个应用程序域都对应着一个ApplicationManager类的实例(通过AppManagerAppDomainFactory创建),负责管理运行在域中的ASP.NET应用程序(启动和停止一个ASP.NET应用程序、创建对象等)。ApplicationManager还会创建一个HostingEnvironment对象,提供了ASP.NET应用程序的一些管理信息(如ASP.NET应用程序的标识、虚拟目录与对应的物理目录等)。

应用程序域创建完成之后,创建 System.Web.Hosting 命名空间中的基于ISAPIRuntime的一个对象,调用其ProcessRequest()方法。在此方法中,根据传入的HTTP请求信息屏蔽了版本差异化后封装实例化了一个HttpWorkerRequest的对象并初始化一个HttpRuntime对象。调用HttpRuntime的ProcessRequestInternal方法。ProcessRequestInternal方法实例化一个HttpContext对象(其构造函数内部又实例化了在ASP.NET编程中非常重要的HttpRequest和HttpResponse对象)、通过HttpApplicationFactory获取HttpApplication对象实例。再调用HttpApplication对象的BeginProcessRequest方法启动整个HTTP请求的处理过程(即HTTP管线模式)。

System.Web.UI.Page类提供了“Requset”和“Response”属性来引用这两个对象,因此在ASP.NET网页中可以直接访问这两个对象。同样,System.Web.UI.Page类的Context属性引用HttpContext对象。

HttpRuntime类的ProcessRequest()方法除了创建HttpContext对象之外,还完成了另一个很重要的工作——向HttpApplicationFactory类的一个实例(它管理一个HttpApplication对象池)申请分配一个HttpApplication对象用于管理整个“HTTP请求处理管线(HTTP Pipeline)”中的各种事件的激发。在HttpApplication对象的InitModules()方法中创建HTTP模块(HTTP Module),通常在HTTP模块对象(实现IHttpModule接口)的Init()方法中响应HttpApplication对象所激发的特定事件。开发者可以创建基于IHttpHandler接口的对象,在Web.Config中插入特定的内容可以将自定义的HTTP模块加入到HTTP请求的处理流程中。 在IIS6和IIS7经典模式下,是用 Event+事件名称做key将所有事件的保存在HttpApplication的Events属性对象里,然后在BuildSteps里统一按照顺序组装。在IIS7集成模式下,是通过HttpModule名称+RequestNotification枚举值作为key将所有的事件保存在HttpApplication的ModuleContainers属性对象里,这个属性的类型是PipelineModuleStepContainer[]。

“HTTP请求处理管线(HTTP Pipeline)”的处理过程为:

  1. 预处理阶段:由多个HTTP模块参与,通过事件来驱动,主要是对HttpContext对象的各种属性进行修改。
  2. 处理阶段:由System.Web.UI.Page类接手HttpContext对象。Page类实现了IHttpHandler接口,因此有ProcessRequest()方法并被自动调用。ProcessRequest()方法的执行结果刷新了HttpContext对象的内容。
  3. 后处理阶段:HttpApplication对象继续激发后继的事件。如果有特定的HTTP模块响应这些事件,则它们会被自动调用。在管线末端,信息传回到 ISAPIRuntime,以及 aspnet_isapi.dll,最后交由 HTTP Listener 回传给客户端。当HTTP请求处理完毕,相关的对象被释放,但创建的应用程序域,以及HttpApplication等对象仍然存活,以响应下一次HTTP请求。

具体的处理管线:

  1. BeginRequest: 开始处理请求。当ASP.NET运行时接收到新的HTTP请求的时候引发这个事件
  2. AuthenticateRequest授权验证请求,获取用户授权信息。当ASP.NET 运行时准备验证用户身份的时候引发这个事件
  3. PostAuthenticateRequest:验证身份成功。通过HttpContext.User获取
  4. AunthorizeRequest 用户权限授权。当ASP.NET运行时准备授权用户访问资源的时候引发这个事件
  5. PostAuthorizeRequest:获得授权
  6. ResolveRequestCache:获取页面缓存结果,如果存在直接返回结果
  7. PostResolveRequestCache 缓存检查结束
  8. MapRequestHandler:IIS7集成模式才支持
  9. 查找并创建HttpHandler:根据URL路径或扩展名和匹配规则查找创建,赋值给HttpContext.Handler属性(仅限于IIS6与IIS7经典模式)
  10. PostMapRequestHandler 成功创建IHttpHanlder对象
  11. AcquireRequestState 获取请求状态,一般用于获取Session:先判断当前页面对象是否实现了IRequiresSessionState接口,如果实现了,则从浏览器发来的请求报文体中获得SessionID,并到服务器的Session池中获得对应的Session对象,最后赋值给HttpContext的Session属性
  12. PostAcquireRequestState 成功获得Session
  13. PreRequestHandlerExecute:准备执行HttpHandler处理程序。
  14. 执行HttpHandler处理程序(仅限于IIS6与IIS7经典模式):调用HttpHandler的ProcessRequest方法(同步)或BeginProcessRequest方法(异步)。
  15. PostRequestHandlerExecute 执行HttpHandler处理程序结束。
  16. ReleaseRequestState 准备释放请求状态(Session)
  17. PostReleaseRequestState 成功释放请求状态(Session)
  18. Filter:将内容写入Filter(仅限于IIS6与IIS7经典模式)
  19. UpdateRequestCache 更新缓存
  20. Filter:将内容写入Filter(仅限于IIS7集成模式)
  21. PostUpdateRequestCache 已更新缓存
  22. LogRequest 为当前请求执行日志记录(仅限于IIS7集成模式)
  23. PostLogRequest 已完成日志(仅限于IIS7集成模式)
  24. EndRequest 完成。把响应内容发送到客户端之前引发这个事件。
  25. Noop:扩展使用(仅限于IIS6与IIS7经典模式)
  26. PreSendRequestHeaders:根据发送的Headers动态设置一些参数(仅限于IIS7集成模式)
  27. PreSendRequestContent:可对输出内容做压缩(仅限于IIS7集成模式)

在 ASP.NET 内部的 HTTP 处理器有:

  • ISAPIRuntime:由 aspnet_isapi.dll 调用,初始化 HttpWorkerRequest 对象(会由IIS的版本决定要初始化的版本)。
  • HttpRuntime:提供请求队列 (Request Queue)、调用 HttpWorkerRequest 中的 ProcessRequest() 方法,以及后续的处理工作。
  • HttpWorkerRequest:产生 HttpApplication、HttpRequest、HttpResponse 等基础对象的 HTTP 请求对象,并将要求转送到要处理的对象(并调用它的 ProcessRequest() 方法)。
  • IHttpHandler 与 IHttpAsyncHandler:负责处理 HTTP 请求的单元,由 ProcessRequest() 来分派与执行请求。

ASP.NET网页中的事件程序

当 HttpWorkerRequest 调用 ASP.NET 网页(System.Web.UI 命名空间的 Page 类别)的 Page.ProcessRequest() 方法时,它会依序的引发 Page 内的各个事件,并同时调用在 Page 中所有控件的相关事件,其引发顺序为[5]

启动阶段:设置页面基本属性,如 Request 和 Response。在此阶段,页面还将确定请求是回发请求还是新请求,并设置 IsPostBack 属性。

  1. PreInit 事件:执行预先初始化的工作,在ASP.NET 2.0中,若要动态调整主版页面 (Master Page)、主题 (Theme) 时,要在这个事件中调整。设置页面一些最基本的特性,如加载个性化信息和主题、运行时Culture。
  2. Init 事件:执行初始化工作。根据页面的服务器标签及其属性设置,生成各个服务器控件的实例和给这些服务器控件的实例的属性进行赋值,并存入ViewState,但因为本阶段的赋值行为优先于ViewState的TrackViewState被调用,所以这些视图的值都将不会存入最终呈现的视图状态隐藏字段中。
  3. InitCompleted 事件:在完成初始化工作后引发。调用ViewState的TrackViewState方法,开启对视图状态更改的跟踪。
  4. Preload 事件:加载表单传值)。页面方法调用LoadViewState、LoadControlState、ProcessPostData,服务器控件对应调用LoadViewState和实现IPostBackDataHandler接口的LoadPostData方法
  5. Load 事件:执行加载的工作,大多数的网页都拥有 Page_Load 事件处理函数,用户控件 (user control) 中也有 Page_Load 事件例程,都会在此时调用。
    控件的 PostBack 变更通知:当网页侦测到是 PostBack 要求时,会引发 PostBack 消息通知的事件。
    控件的 PostBack 相关事件:当网页侦测到是 PostBack 要求时,会引发 PostBack 消息指定的控件的事件。
  6. LoadCompleted 事件:执行加载完成后的工作。
  7. PreRender 事件:处理在产生 HTML 结果前的工作。
  8. SaveStateCompleted 事件:处理页面状态(ViewState 与 ControlState)存储完成后的事件。
  9. Render 事件:处理产生 HTML 的工作。
  10. Unload 事件:处理结束网页处理时的工作。

如果 HttpWorkerRequest 调用的是实现 IHttpHandler 接口的 HTTP 处理函数 时,它只会调用 IHttpHandler.ProcessRequest() 方法,由它来处理程序的输出,不像 Page.ProcessRequest() 会处理事件顺序,因此 HTTP Handler 很适合轻量级的资料处理,像是输出文件资料流或是图片资料流等。

ASP.NET的事件模型

ASP.NET 的原始设计构想,就是要让开发人员能够像 VB 开发工具那样,可以使用事件驱动式程序开发模式 (Event-Driven Programming Model) 的方法来开发网页与应用程序,若要以ASP技术来做到这件事的话,就必须要使用大量的辅助信息,像是查询字符串或是窗体字段资料来识别与判断对象的来源、事件流向以及调用的函数等等,需要撰写的代码量相当的多,但 ASP.NET 很巧妙利用窗体字段和JavaScript脚本把事件的传递模型隐藏起来了。

ASP.NET 的事件模型是由 <form runat="server"></form> 以及数个 Hidden Field 组合而成,基于 HTTP 模型的限制,所有的网页程序在执行结果输出到客户端后,程序就会结束执行,为了维护在 ASP.NET 网页与控件的状态资料,因此在输出 ASP.NET 控件时,ASP.NET 会将部分状态资料存储到网页的 Hidden Field 中,这类型的状态资料称为 ViewState(ID 为 __VIEWSTATE),在服务端即会被解译出状态与事件资料。在大多数的内置 Web 控件中都有使用到这个机制,因此在使用大量 ASP.NET Web 控件的网页中,会有许多的 ViewState 会存放在网页中并随着 HTTP 资料流输出到客户端,ViewState 在输出时,会被加密为一组乱码字符串,其密钥值定义在电脑中,并且每一个对象都会被序列化 (serialize) 成字符串(因此若是自定义对象要放到 ViewState 时,则应要让它支持序列化),再输出到 __VIEWSTATE 字段中,在每次的网页来回时都会被传输,较大的 ViewState 会让网页大小膨胀,不利于快速的网络传输,不过 ASP.NET 本身有提供将 ViewState 关闭的功能,因此如果控件不需要状态保存时,可将它关闭以减少输出的大小。

为确保控件的事件能够确实被引发,让事件驱动能够被执行,因此控件事件引发命令时需要的参数,是交由 JavaScript 脚本在客户端引发时,填入另一个 Hidden Field(ID 为 __EVENTTARGET 以及 __EVENTARGUMENT),并且引发窗体的提交指示 (submit),发送到服务端后,服务端的 HttpApplication 中的工具函数会解析 __EVENTTARGET__EVENTARGUMENT 字段中的信息,并且交由控件所实现的 RaisePostBackEvent() 来引发事件,并由 .NET Framework 内部的事件处理机制接手处理(调用控件设置的事件处理函数)。

ASP.NET的来回模式

在 ASP.NET 运行的时候,经常会有网页的来回动作 (round-trip),在 ASP.NET 中称为 PostBack,在传统的 ASP 技术上,判断网页的来回是需要由开发人员自行撰写,到了 ASP.NET 时,开发人员可以用 Page.IsPostBack 机能来判断是否为第一次执行(当 ASP.NET 发现 HTTP POST 要求的资料是空值时),它可以保证 ASP.NET 的控件事件只会执行一次,但是它有个缺点(基于 HTTP POST 本身的缺陷),就是若用户使用浏览器的刷新功能(按 F5 或刷新的按钮)刷新网页时,最后一次执行的事件会再被执行一次,若要避免这个状况,必须要强迫浏览器清空缓存才可以。

ASP.NET 2.0 中有新增三个来回模式:

  • Cross Page Postback:允许跨不同的网页执行 PostBack,服务端可使用 Page.IsCrossPostBack 来判断是否是跨网页型的来回。
  • Async Page Mode:允许网页使用异步的方式执行,服务端可用 Page.IsAsync 来判断。
  • Callback:ASP.NET 2.0 新增的由网页回调客户端指令的功能,服务端可用 Page.IsCallback 来判断是否要求是来自 Callback。

来回模式不仅是 ASP.NET 运作时的核心,它也是 ASP.NET 应用程序的一个主要缺点,尤其是在设计复杂度高的页面时,在网页中隐藏的 ViewState 的大小会相当大,而在每次的来回动作中,都会发送 ViewState 在内的窗体信息,大量的 ViewState 会使得发送的时间拉长,而且每次来回动作都会让整个网页被刷新,而出现闪烁的情况(就算在本地端也一样),但在AJAX技术尚未成熟时,只能够忍受这种因底层限制所带来的问题,在ASP.NET AJAX技术发展出来后,透过UpdatePanel成功的缓解了这个问题(但 ViewState 发送的问题仍然未根本的解决,必须要使用像 Page Method 这样的方式才能彻底的解决)。

绘制技术

熟悉 ASP 技术的人都知道,代码都是混在 HTML 标签之间,以输出预期需要的 HTML 指令,这个技术在 ASP.NET 中,由各控件的绘制 (Render) 机制包装起来了,绘制机制装载了 HtmlTextWriter 对象,由它来产生 HTML 指令,它会输出至 HttpContext 的 Response 输出资料流中(即 ASP 技术的 Response.Write()),下列代码为绘制技术的示例:

[System.Security.Permissions.PermissionSet(System.Security.Permissions.SecurityAction.Demand, Name="FullTrust")] 
protected override void Render(HtmlTextWriter output) 
{
    if ( (HasControls()) && (Controls[0] is LiteralControl) ) 
    {
        output.Write("<H2>Your Message: " + ((LiteralControl) Controls[0]).Text + "</H2>");
    }
}

为了让控件的输出有层次结构性,ASP.NET 开发了一个可以层次结构化输出控件 HTML 指令的方式,称为控件树 (Control Tree),借由控件树,可以让各个控件的输出可以层次结构化,不论是否是收纳型控件,还是一般型的控件,都可以按照控件的顺序来输出。

状态管理

状态管理 (state management) 在Web应用程序中,一向是很重要的课题,良好的状态管理可以帮助开发人员发展出具有状态持续能力的应用程序(像是工作流程型应用程序或是电子商务应用程序),但状态管理功能会视应用程序的部署状态以及信息的共享程度来选择,在 ASP.NET 中,分为服务端状态管理以及客户端状态管理,客户端状态管理为ViewState以及Cookies,服务端状态管理则是SessionApplication对象。它们的差异点在于:

  • ViewState 是加密的资料流,和 HTML 一起输出到客户端。
  • Cookies 是加密(也可不加密)的小型资料,和 HTML 不同,它可以缓存在客户端浏览器中。
  • Session 是服务端的状态保存机制,每个客户端均有独立的空间(以浏览器执行个体来赋与唯一的SessionID值)。
  • Application 是服务端的状态保存机制,但应用程序所有的客户端共享同一份状态资料。

应用程序层级对象

Application 对象会在应用程序的 Application_OnStart 事件中初始化,并使用名称来识别资料(它是一个 NameObjectCollectionBase 集合的实现品),它会存储在应用程序的范围内,所有的连线(用户)都可以使用,属于共享型的存储体,适合存储所有用户都可使用的资料,在多人使用的情况下,可以适当的使用 Lock/Unlock 的机制来确保应用程序状态的更新。

Application.Lock();
Application["PageRequestCount"] = 
    ((int)Application["PageRequestCount"])+1;
Application.UnLock();

连线层级对象

连线层级的对象是 Session,以浏览器的执行个体为识别单位,资料依浏览器的执行个体来存储,在浏览器的执行个体第一次连到应用程序时,ASP.NET会设置一个Session ID,并且使用它来识别 Session,每一个 Session 都是 ICollection 与 IEnumerate 的实现,用 key 来识别资料值,并且具有时间的限制 (timeout),若超出时限时服务器会自动清理掉,默认的 Session 时限为 20 分钟。Session ID 的算法是由 RNGCryptoServiceProvider(密码编译随机数产生器提供者)产生,并编码成一个 Session ID 字符串(例如 anf4vuup3xiq0arjlqla2l55 这样的字符串)存储在服务器中,用以识别不同的 Session 个体。

为因应不同的客户端,ASP.NET 设计了不同的 Session ID 存放机制,像是旧式的浏览器或是移动客户端这种不支持本地存储cookie的设备时,ASP.NET 可以直接在 URL 中加上 Session ID 的识别,像是 http://www.example.org/(anf4vuup3xiq0arjlqla2l55)/profile.aspx 这样的 URL,可以由开发人员自行设置,或是使用 AutoDetect 设置来让 ASP.NET 自行判断要使用的 Session ID 存放方式。

Session ID 的产生方法可以由程序开发人员自定义,借由改写 SessionIDManager 的 CreateSessionID() 方法来自定义。

using System;
using System.Configuration;
using System.Web.Configuration;
using System.Web;
using System.Web.SessionState;


namespace Samples.AspNet.Session
{

  public class GuidSessionIDManager : SessionIDManager
  {

    public override string CreateSessionID(HttpContext context)
    {
      return Guid.NewGuid().ToString();
    }


    public override bool Validate(string id)
    {
      try
      {
        Guid testGuid = new Guid(id);

        if (id == testGuid.ToString())
          return true;
      }
      catch
      {
      }

      return false;
    }
  }
}

跨机器状态管理

状态管理在单一服务器上,可以存储在服务器的存储器中,但若是在大型网站中,使用许多的 Web 服务器来实行负载平衡(Load Balancing)处理时,会有状态存储在哪个位置的问题,因此需要有一个可以在每个 Web 服务器之间做状态存储的介质,像是独立的服务器或是数据库等等。在 ASP.NET 中支持了四种状态存储的介质[6]

  • InProc:存储与 ASP.NET 相同的执行行程中 (in-procedure state),适合单一服务器的状态存储。
  • StateServer:存储在 ASP.NET 状态服务器 (state server) 中,适合跨服务器的状态存储,但因为它使用的端口,因此在使用上需要注意防火墙的问题。
  • SQLServer:存储在独立的 SQL Server 数据库中,适合跨服务器的状态存储。
  • Custom:以自行实现的状态提供者 (state provider)。

部件

ASP.NET 是开发 Web 应用程序的基础架构 (framework),除了它内部的运作方法外,对外也显露了许多的开发支持,让开发人员可以利用它来发展出许多强大的 Web 应用程序解决方案。

基础部件

网页

ASP.NET 最基础的底层为网页 (Page),网页由 System.Web.UI.Page 类别来提供基础支持,包含了页面的事件以及对象绘制的引发点(Page 类别本身是一个 HTTP Handler 的实现品)。ASP.NET 网页在微软的官方名称中,称为 Web Form,除了是要和Windows Forms作分别以外,同时也明白的刻划出了它的主要功能:“让开发人员能够像开发 Windows Forms 一样的方法来发展 Web 网页”。因此 ASP.NET Page 所要提供的功能就需要类似 Windows Forms 的窗体,每个 Web Form 都要有一个 <form runat="server"></form> 区块,所有的 ASP.NET 服务器控件都要放在这个区域中,这样才可以让 ViewState 等服务器控制能够顺畅的运作。

在网页中也可以使用代码,以类似于ASP时代的撰写方式来开发,此种开发方式称为 inline code,在 ASP.NET 的程序开发模式中,inline code ,要放在 <script runat="server"></script> 区域中,如下列的示例程序:

<%@ Page Language="C#" %>
 
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
 
<script runat="server">
 
    protected void Page_Load(object sender, EventArgs e)
    {
        Label1.Text = DateTime.Now.ToLongDateString();
    }
 
</script>
 
<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
    <title>Sample page</title>
</head>
<body>
    <form id="form1" runat="server">
    <div>
        The current time is: <asp:Label runat="server" id="Label1" />
    </div>
    </form>
 
</body>
</html>

另一种模式则是将代码和网页分离,这种模式称为代码后置 (Code-Behind),这个方法可以将代码独立到一个文件中,网页可以保持较干净的状态,让维护网页程序的复杂度降低很多,在网页的提示指令 (directive) 中,可以设置代码后置的参数,像是 Inherit、CodeFile、Class 等参数。

<%@ Page Language="C#" CodeFile="SampleCodeBehind.aspx.cs" Inherits="Website.SampleCodeBehind" 
AutoEventWireup="true" %>

而一个典型的代码后置例子为:

using System;
 
namespace Website
{
	public partial class SampleCodeBehind : System.Web.UI.Page
	{
		protected override void Page_Load(EventArgs e)
		{
			base.OnLoad(e);
		}
	}
}

使用代码后置模式的设置时,可以让 ASP.NET 执行引擎在加载网页时,由代码后置参数获取对应的类别信息,藉以使用 Reflection 的方式来执行后置的代码。

ASP.NET 可以支持HTMLXHTML两种网页内容,但在Visual Studio.NET中,默认是使用 HTML,但在Visual Studio 2005以后的版本,则一律都改用XHTML格式。

控件

ASP.NET 的内置控件分为两种:

  • HTML 控件 (HTML control):直接在 HTML 标记中加上 runat="server",即可对应到 ASP.NET 指定的 HTML 服务器控件类别,像是:
    • HtmlAnchor:表示 HTML 的 <a>。
    • HtmlButton:表示 HTML 的 <input type="button">。
    • HtmlImage:表示 HTML 的 <img>。
    • HtmlGenericControl:表示没有对应到服务器控件的对象,都会被对应到这个类别。
  • Web 控件 (Web control):由 ASP.NET 提供,比 HTML 控件提供更多的功能,但操作与设置会比 HTML 控件要复杂许多,像是:
    • LinkButton:一种外型为链接 (<a>) 的按钮。
    • CheckBoxList:创建 CheckBox 的菜单的控件。
    • GridView:将资料绑定并产生 HTML 表格的控件。
    • RequiredFieldValidator:检查字段是否有输入资料的控件。

除了内置的控件之外,ASP.NET 也提供了可以自定义的控件架构,并且支持两种控件开发方法:

  • 用户控件 (User control),以 .ascx 为扩展名,可以让开发人员用最轻松的方式来开发控件,优点是开发速度很快,但缺点是无法转散布,且无法加入参考。
  • 自定义控件 (Custom control),可转散布型的控件,是经过编译后的代码 (DLL),可单独转散布,并且可在其他的项目加入参考取用,自定义控件有三种开发模式:
    • 基础控件 (General Control):由 System.Web.UI.Control 类别继承而来,或是由现有的 Web 控件继承而来,像是由 Button 控件继承。
    • 复合控件 (Composite Control):由 System.Web.UI.WebControls.CompositeControl 继承而来,可以用复合的方式来开发控件。
    • 模板控件 (Template Control):可以在控件中使用模板 (template),并应用资料到模板中,支持资料绑定的表达式。

脚本

ASP.NET 的 Web 控件有时会包装一些客户端脚本 (client-side scripting),在控件被绘制时输出到客户端,这些脚本多数被包装在 DLL 的资源档中,并由 ScriptResource.axd 处理函数来输出,开发人员也可以利用 ClientScriptManager(Page.ClientScript 属性)中的方法来添加脚本到网页程序中,常用的方法有:

  • ClientScriptManager.RegisterClientScriptBlock():注册客户端脚本区块 (script block)。
  • ClientScriptManager.RegisterStartupScript():注册在起始时执行的脚本。
  • ClientScriptManager.RegisterOnSubmitStatement():注册在处理窗体发送时要执行的脚本。
  • ClientScriptManager.RegisterClientScriptInclude():注册由外部文件 (.js) 提供的脚本来源。

基本对象

以往在 ASP 中常被使用的五大基本对象,在 ASP.NET 中仍然持续被支持,但它们都换了一个身份来提供:

  • Application:包装了 HttpApplication 对象,在程序中使用 Application 指令获取的对象,都是来自于 HttpContext.Current.Application 属性回传而得。
  • Request:包装了 HttpRequest 对象,在程序中使用 Request 指令获取的对象,都是来自于 HttpContext.Current.Request 属性回传而得。
  • Response:包装了 HttpResponse 对象,在程序中使用 Response 指令获取的对象,都是来自于 HttpContext.Current.Response 属性回传而得。
  • Session:包装了 HttpSessionState 对象,在程序中使用 Session 指令获取的对象,都是来自于 HttpContext.Current.Session 属性回传而得。
  • Server:包装了 HttpServerUtility 对象,在程序中使用 Server 指令获取的对象,都是来自于 HttpContext.Current.Server 属性回传而得。

导览部件

导览部件 (navigation controls)[7] 是在 ASP.NET 2.0 中才出现的功能,包含:

  • 菜单 (Menu):提供内置的鼠标侦测方式,实现层次结构式的菜单功能。
  • 网站地图 (Sitemap):提供可层次化目前浏览位置的功能,可支持由 Web.sitemap 中的资料,或是来自数据库或 XML 资料档中的数据源。
  • 树状查看 (TreeView):提供层次结构化的资料浏览,类似于资源管理器的接口。

应用程序服务

应用程序服务 (application services) 是在 ASP.NET 2.0 中才开始提供,它以 Provider-based Pattern 为主,实现出数个网站的常用服务,包含会员服务 (Membership Service)[8]、角色服务 (role service)[9]与配置文件服务 (profile service)[10] 等。

会员服务由 Membership 以及其资料提供者 MembershipProvider 构成,应用程序使用 Membership 所显露的方法来操作,它会将要求转送给指定的 MembershipProvider 实现来处理,ASP.NET 目前支持来自于数据库的 SqlMembershipProvider(支持 SQL Server)以及来自于活动目录的 ActiveDirectoryMembershipProvider,开发人员也可以自行由 MembershipProvider 继承来实现自定义的会员服务资料提供者。

角色服务与会员服务类似,由 Role 以及其资料提供者 RoleProvider 构成,应用程序使用 Role 所显露的方法操作,由 RoleProvider 实现提供资料服务,目前内置的 RoleProvider 有来自 活动目录 或 XML 文件的 AuthorizationStoreRoleProvider,由 SQL Server 供应资料的 SqlRoleProvider,以及支持 Windows 角色的 WindowsTokenRoleProvider 三种,开发人员可自行实现 RoleProvider 的方法以发展出自定义的角色服务提供者。

配置文件服务是一个特殊的服务,它结合了 .NET Framework 的 CodeDOM 开发模式,以及 System.Web.Profile 命名空间的 ProfileBase、ProfileInfo 与 ProfileManager 等类别,组合出完整的配置文件支持,其数据源也是以 Provider-based Pattern 为主,由 ProfileProvider 提供,ASP.NET 内置由 SQL Server 数据库创建的 SqlProfileProvider,其字段系由开发人员在 ASP.NET 配置文件中自行定义,再由 ASP.NET 动态产生强类型的字段属性。

配置文件服务也是作为 ASP.NET 2.0 的网页组件 (Web Part Framework) 所需要的配置文件存储支持[11],Web Part Framework 可以让开发人员可以开发出具备个性化能力 (Personalization) 的网页配置方案,让用户可以用自行新增与拖放的方式来设计自己的网页布置,所需要的设置存储即由配置文件服务处理。

另一个需要和应用程序服务配合使用的功能为 Web 事件架构 (Web Event Framework)[12],它需要由应用程序服务提供数据结构,它也有 Provider-based Pattern,并可以支持数种的事件资料提供者。

延展性支持

除了 ASP.NET 网页以外,.NET Framework 还提供了两种可以由开发人员自行发展处理模型的模块,一种是HTTP Handler,另一种则是HTTP Module[13]

HTTP Handler(扩展名为 .ashx)由 System.Web.IHttpHandler 接口定义了必要的方法(可支持异步的 HTTP Handler 称为 HTTP Async Handler,由 System.Web.IHttpAsyncHandler 接口定义),其中最重要的方法是 ProcessRequest() 方法,开发人员必须要实现这个方法,才能够让 HTTP Handler 有作用,它也可以透过 ASP.NET 的配置设置,让 HTTP Handler 可以处理特定的扩展名,它可以被视为 .NET Framework 中的 ISAPI Extensions 实现方法。

HTTP Module 则是由 System.Web.IHttpModule 接口定义,它可以在整个网页生命周期中被调用多次,并实际处理由 HttpApplication 所引发的事件,开发人员需要实现 IHttpModule.Init() 方法,以及处理 HttpApplication 事件需要的代码。它可被视为 .NET Framework 中的 ISAPI Filter 实现方法。

一致性与多样性接口的支持 

ASP.NET 在一开始的时候是缺乏模板引擎 (template engine) 的,其主因是.NET Framework本身是面向对象,且需要用继承的方式才能够延伸功能,大多数的开发人员都是由 System.Web.UI.Page 继承并定义出新的基类,并撰写要绘制 HTML 的方法,以及在他们的应用程序中修改以继承该类别,然而这个方法可能会被用在网站的很多地方,因而会大大的提升混合代码与标记的复杂度,这个方法也只能在执行期才能够以可视化的方式测试,无法在设计时期可视化,其他的开发人员总是使用原有的 ASP 方法(即<!-- #include --> 指令)来把每个网页需要的部分包到网页中,防止在每个网页中都要撰写相同的导览代码。

在 ASP.NET 2.0 中,推出了母板页面 (master page)的概念[14],它可以让开发人员先行定义外观版型 (*.master),再使用它来应用实际执行的网页,网页与主版页面之间以 ContentPlaceHolder 的 ID 做链接,以应用正确的内容到保留区(即由 ContentPlaceHolder 包住的区域)中,开发人员也可以定义在保留区没有应用时需要显示的默认内容。在 ASP.NET 3.5 中更进一步的支持设计时期的嵌套主版页面 (nested master pages),以及把网页的 HEAD 区块纳入 ContentPlaceHolder 的范围。

与主版页面相关的,还有主题(Theme)以及面板(skin)技术[15],这两个技术允许开发人员或设计人员自行定义网页的样式设置以及应用的样式支持,每个主题中可以包含数个面板档,这些面板档决定了控件要输出时应用的样式,开发人员则可以利用主题来决定不同的外观要使用的样式。

ASP.NET 也允许在应用程序中动态的变更主版页面与主题,但必须要在页面的 PreInit 事件例程设置。

void Page_PreInit(Object sender, EventArgs e)
{
    Page.MasterPageFile = "~/NewMaster.master";
    Page.Theme = "MyTheme";
}

编译模型

ASP.NET 在 1.x 时,使用的是组件为主的编译方式,一个网页只会产生一个组件,这个方式最大的优点,就是可以自由定义命名空间,且在部署应用程序时会比较方便,但由于 ASP.NET 1.x 所处的时代,如果网站是有许多代码的情况下(即 DLL 档很大),加载的速度会变慢,且占用存储器的量会很多,当时的存储器价格也尚未降到现在的水准。因此在 ASP.NET 2.0 开始,另外提供了一个预先编译(Pre-compilation) 的编译模型,这个编译方法会将每个网页都各自编译成一个组件,其文件名称会是 App_[随机数字符串].dll 命名,在编译时期由 ASP.NET Pre-compilation 工具(aspnet_compiler.exe)给定,优点是可以不必加载过量的代码到存储器中,但缺点则是无法自定义命名空间,而且在更新时必须要更新所有的 DLL 档以及网页等,否则会造成名称不一致,让 DLL 无法被加载的问题。

早期 ASP.NET 2.0 仅提供预先编译模式,让它的缺点很快的被暴露出来,因此微软也为 ASP.NET 2.0 开发了沿用 ASP.NET 1.x 的编译模型的工具:Web Application Project,在 Visual Studio 2008 中开始内置,至此,ASP.NET 支持两种编译模式的架构抵定。

安全性支持

ASP.NET 的安全性支持分为验证 (Authentication)授权 (Authorization)两个部分。

验证

ASP.NET 的验证方式有三种[16]

  • Windows 验证:由 IIS 目前执行的账户,或者是用户模拟 (user impersonate) 账户的方式进行验证。
  • 窗体验证:由窗体的资料提供验证,开发人员自定义验证逻辑,并交由 ASP.NET 窗体验证工具写入验证证书,以进行授权。
  • Passport 验证:在 ASP.NET 1.x 中,连接Windows Live ID(当时的旧称为Microsoft Passport)服务以进行验证。

授权

ASP.NET 的授权方式有两种[17]

  • 文件授权:由 ASP.NET 检查文件的 访问控制表 (ACL) 来授权访问权限。
  • URL授权:由开发人员设置的 URL 来给予权限。

一个 URL 授权的设置示例如下:

<authorization>
  <allow users="Kim"/>
  <allow roles="Admins"/>
  <deny users="John"/>
  <deny users="?"/>
</authorization>

Web Service支持

ASP.NET 1.0 开始支持 Web Service 的开发,是微软在原生平台上支持 Web Service 发展的第一个实现品,但它却不是微软的第一个 Web Service 开发工具实现品[18],.NET Framework 中提供了一个 WSDL.exe,可以连接 Web Service 下载WSDL定义档,并产生一个 Proxy Class 的源代码,供客户端应用程序使用,若是使用 Visual Studio 开发的话,这个动作会由“加入 Web 参考”的动作在背后处理掉。

ASP.NET Web Service 的发展只是平台的基础,微软在 Web Service 的开发上提供持续的支持,尤其是在 WS-I (Web Service Interoperability) 组织成立后,为符合 WS-I 的 Web Service 标准,微软开发了强化 Web Service 的增强包 Web Service Enhancement (WSE),最新版本为 3.0 版(搭配 ASP.NET 2.0),可支持许多 WS-I 的标准。

由于 Windows Communication Foundation 的推出,微软将 Web Service 的发展重心移到 WCF 上,原有的 ASP.NET Web Service 即给定了一个名称:ASMX Web Service

扩展

ASP.NET 在 2.0 版时,功能已大致底定,成为 Web 应用程序的基础架构,微软开始在 ASP.NET 2.0 上开发扩展的功能,包括 AJAX 的支持、MVC架构的支持以及更容易开发出数据库应用的架构。

ASP.NET AJAX

ASP.NET AJAX 是微软发展的 AJAX Framework,让 ASP.NET 的开发人员得以用很简单的方式就可以开发出支持 AJAX (AJAX-enabled) 的应用程序,包含客户端脚本的支持,以及服务端的链接等等。

ASP.NET MVC Framework

ASP.NET MVC Framework 是微软基于 MVC (Model-View-Controller) 架构所开发的架构,让应用程序各个模型可以在 MVC 架构下运行。

  • View:负责显示资料以及用户界面,在 ASP.NET MVC 架构下,View 可以支持 REST 样式的 URL。
  • Model:负责定义资料的存储,此部分可以由 LINQ to SQL 与 ADO.NET Entity Framework 来代替。
  • Controller:负责处理 View 和 Model 之间的联系。

ASP.NET MVC Framework 也支持以测试驱动的开发模式 (Test-Driven Development)。

ASP.NET Dynamic Data Framework

ASP.NET Dynamic Data Framework 是微软在 ASP.NET 3.5 中开发的一组类别库,封装在 System.Web.DynamicData 命名空间中,并且配合 ASP.NET Routing Model(网页绕送功能)让开发人员可以很简单的开发出基于 LINQ to SQL 或是 ADO.NET Entity Framework 资料模型的数据库应用程序。

ASP.NET Routing

ASP.NET Routing Model(官方译名为 ASP.NET 路由)是一个基于REST规格下的 URL 对应机制,开发人员可以在服务端设置 URL 的格式,用户可以用由开发人员定义的 URL 格式浏览网页,ASP.NET 会自动将 URL 转换成为内部的 URL 格式,虽然它和 URL Rewriting 很像,但微软认为 ASP.NET Routing 不是 URL Rewriting[19]

Silverlight

Silverlight 是微软的新一代RIA技术,ASP.NET 3.5 Service Pack 1 (SP1) 中加入了对 Silverlight 2.0 的 ASP.NET 服务端支持,包含:

  • Silverlight控件:让服务端可以产生支持 Silverlight 的对象标记,以及自定义参数等。
  • Media控件:让服务端输出以 Silverlight 为主的流影音 (streaming media) 播放器。

开发工具

目前已有数个工具可支持 ASP.NET 应用程序的开发。

版本

在一台计算机上,查看Windows注册表

   HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\NET Framework Setup\NDP\v4\Full

其Version节点显示的就是所安装的ASP.NET的版本号。

日期 版本 帮助 新功能
2002年1月16日 1.0 Visual Studio .NET一起发行的第一个版本
  • 面向对象的Web应用程开发,支持继承多态与其他标准面向对象编程的功能。
  • 开发人员不再需要使用 Server.CreateObject(...),让早期绑定与类型安全变得可能。
  • Microsoft Windows程序设计为基础,开发人员能够使用在Web Server上使用的DLL类别库来创建许多能够做到比只简单的输出 HTML 更多的鲁棒性 (robust) 应用程序(例如异常处理功能)。
2003年4月24日 1.1 Windows Server 2003Visual Studio .NET 2003一起发表。
  • 移动设备控件。
  • 自动化输入验证。
2005年11月7日 2.0

研发代号为Whidbey,和Visual Studio 2005Visual Web Developer ExpressSQL Server 2005一起发表。

  • 新的资料控件(GridView、FormView、DetailsView)
  • 新的宣告式资料访问技术(SqlDataSource、ObjectDataSource 与 XmlDataSource 控件)
  • 导览控件(SiteMap、Menu、TreeView)
  • 主版页面 (Master Page)
  • 登录控件
  • 主题
  • 表皮 (skin)
  • Web 部件 (Web Part)
  • 个性化服务 (Profile)
  • 全功能的预先编译能力
  • 全新的当地语系化技术
  • 支持64位平台
  • 提供者类别模式
2007年11月19日 3.5 Visual Studio 2008Windows Server 2008一起发表
  • 新资料控件(ListView、DataPager)
  • ASP.NET AJAX 内含到.NET Framework,成为.NET Framework的一部分。
  • 提供支持 LINQ 的 LinqDataSource 控件。
2008年8月11日 3.5 Service Pack 1[21] 于 Visual Studio 2008 Service Pack 1 发表
  • 内含ASP.NET Dynamic Data
  • 在 ASP.NET AJAX 应用程序中支持浏览器历史控制 (controlling browser history)。
  • 将多个JavaScript文件合并到单一文件以强化有效的下载。
2010年4月12日 4.0 Visual Studio 2010一起发表
  • 配合.NET Framework 4.0让Web应用程序具有如并行运算函式库(Parallel Library)等新功能。
  • ASP.NET MVC 2.0
  • jQuery完全集成与ASP.NET AJAX Client Library 强化,以及 AJAX CDN 的支持。
  • ASP.NET 的 Render Compatibility (3.5以前版本或4.0版),可控制许多 ASP.NET Web 控件的绘制行为,以配合标准 HTML 与 jQuery 的处理。
  • SEO的支持。
  • 自定义缓存提供者 (Extensible Output Cache)。
  • QueryExtender 的支持。
  • CSS 控制行为的变更。
  • 自定义的 Client ID 输出。
  • ViewState 的控制。
  • 配合 Visual Studio 2010 的 Web Deploy 工具。
  • Entity Framework 4.0 的支持。
  • Dynamic Data Framework 与 Chart Control 内置至核心中。
2012年8月15日 4.5 Visual Studio 2012一起发表
2013年10月17日 旧版本,不再支持: 4.5.1 Visual Studio 2013[22]发布用于Windows Server 2012 R2Windows 8.1
2014年5月5日[23] 旧版本,不再支持: 4.5.2
  • 更高可靠性的HTTP header检查与修改方法
  • 新的方式调度背景异步worker任务
2015年7月29日[23] 旧版本,不再支持: 4.6 发布于[24]Visual Studio 2015[25]EF 7预览版用于Windows Server 2016Windows 10
  • HTTP/2支持运行于 Windows 10
  • 更多的异步任务返回API
2015年11月30日[23] 旧版本,不再支持: 4.6.1
2016年8月2日[23] 旧版本,仍被支持: 4.6.2
  • 改进的异步支持(输出cache与session providers)
2017年4月11日[23] 旧版本,仍被支持: 4.7 包含在Windows 10 Creators Update[26]
  • 操作系统支持TLS协议
2017年10月17日[23] 当前版本: 4.7.1 包含在Windows 10 Fall Creators Update.[27]
  • 改进可访问性
  • Value tuple 类型序列化
  • SHA-2 支持
2018年5月1日[28] 当前版本: 4.7.2
2019年4月18日[29] 当前版本: 4.8 包含在 Windows 10 May 2019 Update 修复了ASP.NET的10个bugs
2015年11月18日 旧版本,不再支持: 5 RC1 这一版本后来从ASP.NET分开,改称ASP.NET Core 1.0.[30] 一个全新项目有不同的开发原则与目标。
格式:
旧版本
旧版本,仍被支持
当前版本
最新的预览版
未来版本

参见

参考资料

  1. ^ Release 3.3.0. 2023年10月24日 [2023年11月18日]. 
  2. ^ MONO Project. [2009-01-18]. (原始内容存档于2010-05-27). 
  3. ^ Architecture Journal Profile: Scott Guthrie. [2009-01-18]. (原始内容存档于2009-01-12). 
  4. ^ 若是 IIS 5.x 时,则是转交给网站的线程,IIS 6.0 以后的版本才有 Worker Process 的工作模型
  5. ^ ASP.NET Page Lifecycle Overview. [2009-01-18]. (原始内容存档于2009-01-18). 
  6. ^ 工作階段狀態模式. [2009-01-18]. (原始内容存档于2009-03-12). 
  7. ^ ASP.NET 網站巡覽. [2009-01-18]. (原始内容存档于2009-06-25). 
  8. ^ 使用成員資格管理使用者. [2009-01-18]. (原始内容存档于2008-12-25). 
  9. ^ 使用角色管理授權. [2009-01-18]. (原始内容存档于2008-12-25). 
  10. ^ ASP.NET 設定檔屬性概觀. [2009-01-18]. (原始内容存档于2009-06-25). 
  11. ^ Web 組件個人化概觀. [2009-01-18]. (原始内容存档于2009-06-24). 
  12. ^ ASP.NET 健康監視事件概觀. [2009-01-18]. (原始内容存档于2008-12-25). 
  13. ^ HTTP 處理常式和 HTTP 模組概觀. [2009-01-18]. (原始内容存档于2009-02-28). 
  14. ^ ASP.NET 主版頁面. [2009-01-18]. (原始内容存档于2009-06-24). 
  15. ^ ASP.NET 佈景主題和面板概觀. [2009-01-18]. (原始内容存档于2008-12-25). 
  16. ^ ASP.NET 驗證. [2009-01-18]. (原始内容存档于2009-06-24). 
  17. ^ ASP.NET 授權. [2009-01-18]. (原始内容存档于2009-06-25). 
  18. ^ 早期还有一个在 ASP 上发展 Web Service 的工具,称为 SOAP Toolkit。
  19. ^ ASP.NET 路由. [2009-01-18]. (原始内容存档于2009-01-23). 
  20. ^ VS Express FAQ. [2009-01-18]. (原始内容存档于2009-01-23). 
  21. ^ ASP.NET in .NET 3.5 Service Pack 1. Microsoft. [2009-01-18]. (原始内容存档于2009-01-21). 
  22. ^ Announcing release of ASP.NET and Web Tools for Visual Studio 2013. [2018-10-16]. (原始内容存档于2015-12-08). 
  23. ^ 23.0 23.1 23.2 23.3 23.4 23.5 .net framework product lifecycle. [2018-10-16]. (原始内容存档于2018-10-16). 
  24. ^ Announcing .NET Framework 4.6. [2018-10-16]. (原始内容存档于2015-07-20). 
  25. ^ Visual Studio 2015 and Visual Studio 2013 Update 5 Released. msdn.com. Microsoft. [2018-10-16]. (原始内容存档于2015-09-04). 
  26. ^ Announcing the .NET Framework 4.7. [2018-10-16]. (原始内容存档于2018-07-10). 
  27. ^ Announcing the .NET Framework 4.7.1. [2018-12-21]. (原始内容存档于2017-12-31). 
  28. ^ Microsoft .NET Framework 4.7.2 正式版发布 2018年05月01日 cnBeta.COM. [2019年5月14日]. (原始内容存档于2019年5月14日). 
  29. ^ Announcing the .NET Framework 4.8. [2019-05-15]. (原始内容存档于2019-04-19). 
  30. ^ Releases. GitHub. [2018-10-16]. (原始内容存档于2018-04-10). 

外部链接