一起谈.NET技术,ASP.NET Process Model之二:ASP.NET Http Runtime Pipeline[下篇]

简介:   ASP.NET Process Model索引ASP.NET Process Model之一:IIS 和 ASP.NET ISAPIASP.NET Process Model之二:ASP.NET Http Runtime Pipeline[上篇]ASP.

  ASP.NET Process Model索引

  二、ASP.NET Runtime Pipeline

  现在我们真正进入ASP.NET管辖的范畴,下图基本上囊括整个处理过程涉及的对象,接下来我们一起来讨论这一系列的对象如何相互协作去处理Http Request,并最终生成我们所需的Http Response。

  HttpContext

  上面我们介绍了ISAPI在调用ISAPIRuntime的时候将对应的ISAPI ECB Pointer作为参数传递给了ProcessRequest方法,这个ECB pointer可以看成是托管环境和非托管环境进行数据交换的唯一通道,Server Variable和Request Parameter通过它传入ASP.NET作为进一步处理的依据,ASP.NET最后生成的Response通过它传递给ISAPI,并进一步传递给IIS最终返回到Client端。

  借助这个传进来的ECB Pointer,我们创建了一个ISAPIWorkerRequest。ISAPIWorkerRequest作为参数传入HttpRuntime.ProcessRequestNoDemand的调用。HttpRuntime.ProcessRequestNoDemand最终体现在调用ProcessRequestInternal。下面是真个方法的实现:

  
  
ProcessRequestInternal
private void ProcessRequestInternal(HttpWorkerRequest wr)
{
HttpContext context;
try
{
context
= new HttpContext(wr, false );
}
catch
{
wr.SendStatus(
400 , " Bad Request " );
wr.SendKnownResponseHeader(
12 , " text/html; charset=utf-8 " );
byte [] bytes = Encoding.ASCII.GetBytes( " <html><body>Bad Request</body></html> " );
wr.SendResponseFromMemory(bytes, bytes.Length);
wr.FlushResponse(
true );
wr.EndOfRequest();
return ;
}
wr.SetEndOfSendNotification(
this ._asyncEndOfSendCallback, context);
Interlocked.Increment(
ref this ._activeRequestCount);
HostingEnvironment.IncrementBusyCount();
try
{
try
{
this .EnsureFirstRequestInit(context);
}
catch
{
if ( ! context.Request.IsDebuggingRequest)
{
throw ;
}
}
context.Response.InitResponseWriter();
IHttpHandler applicationInstance
= HttpApplicationFactory.GetApplicationInstance(context);
if (applicationInstance == null )
{
throw new HttpException(SR.GetString( " Unable_create_app_object " ));
}
if (EtwTrace.IsTraceEnabled( 5 , 1 ))
{
EtwTrace.Trace(EtwTraceType.ETW_TYPE_START_HANDLER, context.WorkerRequest, applicationInstance.GetType().FullName,
" Start " );
}
if (applicationInstance is IHttpAsyncHandler)
{
IHttpAsyncHandler handler2
= (IHttpAsyncHandler) applicationInstance;
context.AsyncAppHandler
= handler2;
handler2.BeginProcessRequest(context,
this ._handlerCompletionCallback, context);
}
else
{
applicationInstance.ProcessRequest(context);
this .FinishRequest(context.WorkerRequest, context, null );
}
}
catch (Exception exception)
{
context.Response.InitResponseWriter();
this .FinishRequest(wr, context, exception);
}
}

   对象上面的代码没有必要深究,我们只需要了解大体的执行流程就可以了,下面这一段伪代码基本上体现整个执行过程:

   
   
HttpContext context = new HttpContext(wr, false );
IHttpHandler applicationInstance
= HttpApplicationFactory.GetApplicationInstance(context);

  首先通过创建的ISAPIWorkerRequest创建按一个HttpContext对象,随后通过HttpApplicationFactory.GetApplicationInstance创建一个IHttpHandler对象(一般情况下就是一个HttpApplication对象)。

  正如他的名字体现的,HttpContext体现当前Request的上下文信息,它的生命周期知道整个Request处理结束或者处理超时。通过HttpContext对象我们可以访问属于当前Request的一系列常用的对象:Server,Session,Cache,Application,Request,Response,Trace,User,Profile等等。此外我们可以认为将一些数据放在Items属性中作为状态管理的一种方式,不过这种状态管理和其他一些常用的方式,比如Session,Cache,Application,Cookie等,具有根本性的不同之处是其生命周期仅仅维持在当前Request的Context中。

  HttpApplication

  就像其名称体现的一样,HttpApplication基本上可以看成是真个ASP.NET Application的体现。HttpApplication和置于虚拟根目录的Gloabal.asax对应。通过HttpApplicationFactory.GetApplicationInstance创建一个基于Gloabal.asax的HttpApplication对象。在HttpApplicationFactory.GetApplicationInstance方法返回创建的HttpApplication对象之前,会调用一个名为InitInternal的内部方法,该方法会做一些列的初始化的操作,在这些初始化操作中,最典型的一个初始化方法为InitModules(),该方法的主要的目的就是查看Config中注册的所有HttpModule,并根据配置信息加载相应的Assembly,通过Reflection创建对应的HttpModule,并将这些Module加到HttpApplication 的_moduleCollection Filed中。

  HttpApplication本身并包含对Request的任何处理,他的工作方式是通过在不同阶段出发不同Event来调用我们注册的Event Hander。

  下面列出了HttpApplication所有的Event,并按照触发的时间先后顺序排列:

  • BeginRequest:
  • AuthenticateRequest & Post AuthenticateRequest
  • AuthorizeRequest & Post AuthorizeRequest
  • ResolveRequestCache & Post ResolveRequestCache
  • PostMapRequestHandler:
  • AcquireRequestState & AcquireRequestState:
  • PreRequestHandlerExecute & Post RequestHandlerExecute:
  • ReleaseRequestState & Post ReleaseRequestState
  • UpdateRequestCache & PostUpdateRequestCache
  • EndRequest:

  ASP.NET Application, AppDomain and HttpApplication

  对于一个ASP.NET Application来说,一个Application和一个虚拟目录相对应,那么是不是一个Application 对应着一个AppDomain呢?一个Application是否就唯一对应一个Httpapplication对象呢?答案是否定的。

  我们首先来看看Application和HttpApplication的关系,虽然我们对一个Application的Request最终都由一个HttpApplication对象来承载。但不能说一个Application就唯一对应一个固定的HttpApplication对象。原因很简单,ASP.NET天生具有多线程的特性,需要通过相应不同的Client的访问,如果我们只用一个HttpApplication来处理这些并发的请求,会对Responsibility造成严重的影响,通过考虑到Performance的问题,ASP.NET对HttpApplication的使用采用Pool的机制:当Request到达,ASP.NET会现在HttpApplication Pool中查找未被使用的HttpApplication对象,如果没有,则创建之,否则从Pool直接提取。对于Request处理完成的HttpApplication对象,不会马上销毁,而是把它放回到Pool中供下一个Request使用。

  对于Application和AppDomain的关系,可能你会说一个Application肯定只用运行在一个AppDomain之中。在一般情况下这句话无可厚非,但是这却忽略了一种特殊的场景:在当前Application正在处理Request的时候,我们把web.config以及其他一些相关文件修改了,而且这种改变是可以马上被ASP.NET检测到的,为了使我们的变动能够及时生效,对于改动后的第一个Request,ASP.NET会为期创建一个新的AppDomain,而对于原来的AppDomain,也许还在处理修改前的Request,所有原来的Appdomain会持续到将原来的Request处理结束之后,所以对于一个Application,可能出现多个AppDomain并存的现象。

  HttpModule

  我们上面提到HttpApplication就是一个ASP.NET Application的体现,HttpApplication本身并不提供对Request的处理功能,而是通过在不同阶段出发不同的Event。我们能做的只能是根据我们具体的需求将我们的功能代码作为Event Handler注册到需要的HttpApplication Event上面。注册这些Event Handler,我们首先想到的肯定就直接在HttpApplication对应的Global.asax中定义我们的EventHandler好了。这是最直接的办法,而且Global.asax提供一个简洁的方式是我们的实现显得简单:不需要向一般注册Event一样将Delegate添加到对应的Event上面,而是直接通过方法名称和对应的Event匹配的方式直接将对应的方法作为相关的Event Handler。比如Application_ AcquireRequestState就是AcquireRequestState Event handler。

  但是这种方式在很多情况下却达不到我们的要求,更多地,我们需要的是一种Plug-in的实现方式:我们在外部定义一些Request Processing的功能,需要直接运用到我们的Application之中。通过使用HttpModule封装这些功能模块,并将其注册到我们的Application的发式可以很简单的实现这种功能。

  HttpModule实现了System.Web.IHttpModule interface,该Interface很简单,仅仅有两个成员:

  
  
[AspNetHostingPermission(SecurityAction.InheritanceDemand, Level = AspNetHostingPermissionLevel.Minimal), AspNetHostingPermission(SecurityAction.LinkDemand, Level = AspNetHostingPermissionLevel.Minimal)]
public interface IHttpModule
{
// Methods
void Dispose();
void Init(HttpApplication context);
}

我们只要在Init方法中注册相应的HttpApplication Event Handler就可以了:


public class BasicAuthCustomModule : IHttpModule
{
public void Init(HttpApplication application)
{
application.AuthenticateRequest
+=
new EventHandler( this .OnAuthenticateRequest);
}
public void Dispose() { }

public void OnAuthenticateRequest( object source, EventArgs eventArgs)
{

}
}

  所有的HttpModule同machine.config或者Web.config的httpModules Section定义,下面是Machine.config定义的所有httpModule。

  
  
< httpModules >
< add name ="OutputCache" type ="System.Web.Caching.OutputCacheModule" />
< add name ="Session" type ="System.Web.SessionState.SessionStateModule" />
< add name ="WindowsAuthentication" type ="System.Web.Security.WindowsAuthenticationModule" />
< add name ="FormsAuthentication" type ="System.Web.Security.FormsAuthenticationModule" />
< add name ="PassportAuthentication" type ="System.Web.Security.PassportAuthenticationModule" />
< add name ="UrlAuthorization" type ="System.Web.Security.UrlAuthorizationModule" />
< add name ="FileAuthorization" type ="System.Web.Security.FileAuthorizationModule" />
< add name ="ErrorHandlerModule" type ="System.Web.Mobile.ErrorHandlerModule, System.Web.Mobile, Version=1.0.5000.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a" />
</ httpModules >

  但是HttpModule如何起作用的,我们来回顾一下上面一节介绍的:HttpApplicationFactory.GetApplicationInstance方法返回创建的HttpApplication对象之前,会调用一个名为InitInternal的内部方法,该方法会做一些列的初始化的操作,在这些初始化操作中,最典型的一个初始化方法为InitModules(),该方法的主要的目的就是查看Config中注册的所有HttpModule,并根据配置信息加载相应的Assembly,通过Reflection创建对应的HttpModule,并将这些Module加到HttpApplication 的_moduleCollection Filed中,最后依次调用每个HttpModule的Init方法。下面是其实现:

  
  
private void InitModules()
{
this ._moduleCollection = RuntimeConfig.GetAppConfig().HttpModules.CreateModules();
this .InitModulesCommon();
}


private void InitModulesCommon()
{
int count = this ._moduleCollection.Count;
for ( int i = 0 ; i < count; i ++ )
{
this ._currentModuleCollectionKey = this ._moduleCollection.GetKey(i);
this ._moduleCollection[i].Init( this );
}
this ._currentModuleCollectionKey = null ;
this .InitAppLevelCulture();
}

  HttpHandler

  如果说HttpModule关注的是所有Inbound Request的处理的话,Handler确实关注基于某种类型的ASP.NET Resource的Request。比如一个.apsx的Web Page通过一个System.Web.UI.Page来处理。HttpHandler和他所处理的Resource通过Config中的system.web/handlers section来定义,下面是Machine.config中的定义。

  
  
< httpHandlers >
< add verb ="*" path ="trace.axd" type ="System.Web.Handlers.TraceHandler" />
< add verb ="*" path ="*.aspx" type ="System.Web.UI.PageHandlerFactory" />
< add verb ="*" path ="*.ashx" type ="System.Web.UI.SimpleHandlerFactory" />
< add verb ="*" path ="*.asmx" type ="System.Web.Services.Protocols.WebServiceHandlerFactory, System.Web.Services, Version=1.0.5000.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a" validate ="false" />
< add verb ="*" path ="*.rem" type ="System.Runtime.Remoting.Channels.Http.HttpRemotingHandlerFactory, System.Runtime.Remoting, Version=1.0.5000.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" validate ="false" />
< add verb ="*" path ="*.soap" type ="System.Runtime.Remoting.Channels.Http.HttpRemotingHandlerFactory, System.Runtime.Remoting, Version=1.0.5000.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" validate ="false" />
< add verb ="*" path ="*.asax" type ="System.Web.HttpForbiddenHandler" />
< add verb ="*" path ="*.ascx" type ="System.Web.HttpForbiddenHandler" />
< add verb ="GET,HEAD" path ="*.dll.config" type ="System.Web.StaticFileHandler" />
< add verb ="GET,HEAD" path ="*.exe.config" type ="System.Web.StaticFileHandler" />
< add verb ="*" path ="*.config" type ="System.Web.HttpForbiddenHandler" />
< add verb ="*" path ="*.cs" type ="System.Web.HttpForbiddenHandler" />
< add verb ="*" path ="*.csproj" type ="System.Web.HttpForbiddenHandler" />
< add verb ="*" path ="*.vb" type ="System.Web.HttpForbiddenHandler" />
< add verb ="*" path ="*.vbproj" type ="System.Web.HttpForbiddenHandler" />
< add verb ="*" path ="*.webinfo" type ="System.Web.HttpForbiddenHandler" />
< add verb ="*" path ="*.asp" type ="System.Web.HttpForbiddenHandler" />
< add verb ="*" path ="*.licx" type ="System.Web.HttpForbiddenHandler" />
< add verb ="*" path ="*.resx" type ="System.Web.HttpForbiddenHandler" />
< add verb ="*" path ="*.resources" type ="System.Web.HttpForbiddenHandler" />
< add verb ="GET,HEAD" path ="*" type ="System.Web.StaticFileHandler" />
< add verb ="*" path ="*" type ="System.Web.HttpMethodNotAllowedHandler" />
</ httpHandlers >

  需要注意的是,我们不但可以单纯地定义一个实现了System.Web.IHttpHandler的Type,也可以定义一个实现了System.Web.IHttpHandlerFactory 的Type。System.Web.UI.Page是一个典型的Httphandler,相信对此大家已经很熟悉了。在最后还说说另一个典型的HttpHandler:System.Web.HttpForbiddenHandler,从名称我们不难看出,它用于那些禁止访问的Resource,现在应该知道了为了Global.asax不同通过IIS访问了吧。

目录
相关文章
|
域名解析 缓存 Linux
如何让你的.NET WebAPI程序支持HTTP3?
如何让你的.NET WebAPI程序支持HTTP3?
345 2
如何让你的.NET WebAPI程序支持HTTP3?
|
中间件 Go 开发者
Go net http包
Go net http包
337 0
|
12月前
|
JSON 编解码 API
Go语言网络编程:使用 net/http 构建 RESTful API
本章介绍如何使用 Go 语言的 `net/http` 标准库构建 RESTful API。内容涵盖 RESTful API 的基本概念及规范,包括 GET、POST、PUT 和 DELETE 方法的实现。通过定义用户数据结构和模拟数据库,逐步实现获取用户列表、创建用户、更新用户、删除用户的 HTTP 路由处理函数。同时提供辅助函数用于路径参数解析,并展示如何设置路由器启动服务。最后通过 curl 或 Postman 测试接口功能。章节总结了路由分发、JSON 编解码、方法区分、并发安全管理和路径参数解析等关键点,为更复杂需求推荐第三方框架如 Gin、Echo 和 Chi。
|
中间件 Go
Golang | Gin:net/http与Gin启动web服务的简单比较
总的来说,`net/http`和 `Gin`都是优秀的库,它们各有优缺点。你应该根据你的需求和经验来选择最适合你的工具。希望这个比较可以帮助你做出决策。
681 35
|
Go
在golang中发起http请求以获取访问域名的ip地址实例(使用net, httptrace库)
这只是追踪我们的行程的简单方法,不过希望你跟着探险家的脚步,即使是在互联网的隧道中,也可以找到你想去的地方。接下来就是你的探险之旅了,祝你好运!
646 26
|
JSON 数据格式
.net HTTP请求类封装
`HttpRequestHelper` 是一个用于简化 HTTP 请求的辅助类,支持发送 GET 和 POST 请求。它使用 `HttpClient` 发起请求,并通过 `Newtonsoft.Json` 处理 JSON 数据。示例展示了如何使用该类发送请求并处理响应。注意事项包括:简单的错误处理、需安装 `Newtonsoft.Json` 依赖,以及建议重用 `HttpClient` 实例以优化性能。
618 2
|
数据采集 API 开发者
.NET 8新特性:使用ConfigurePrimaryHttpMessageHandler定制HTTP请求
在.NET 8中,通过`ConfigurePrimaryHttpMessageHandler`方法,开发者能更精细地控制HTTP请求,这对于构建高效爬虫尤为重要。此特性支持定制代理IP、管理Cookie与User-Agent,结合多线程技术,有效应对网络限制及提高数据采集效率。示例代码展示了如何设置代理服务器、模拟用户行为及并发请求,从而在遵守网站规则的同时,实现快速稳定的数据抓取。
348 0
.NET 8新特性:使用ConfigurePrimaryHttpMessageHandler定制HTTP请求
|
数据采集 开发框架 .NET
HttpClient在ASP.NET Core中的最佳实践:实现高效的HTTP请求
在现代Web开发中,高效可靠的HTTP请求对应用性能至关重要。ASP.NET Core提供的`HttpClient`是进行这类请求的强大工具。本文探讨其最佳实践,包括全局复用`HttpClient`实例以避免性能问题,通过依赖注入配置预设头部信息;使用代理IP以防IP被限制;设置合理的`User-Agent`和`Cookie`来模拟真实用户行为,提高请求成功率。通过这些策略,可显著增强爬虫或应用的稳定性和效率。
505 0
HttpClient在ASP.NET Core中的最佳实践:实现高效的HTTP请求
|
API
使用`System.Net.WebClient`类发送HTTP请求来调用阿里云短信API
使用`System.Net.WebClient`类发送HTTP请求来调用阿里云短信API
379 0