本系列文章从源码分析的角度来探索 ASP.NET Core 的运行原理,分为以下几个章节:

ASP.NET Core 运行原理解剖[1]:Hosting

ASP.NET Core 运行原理解剖[2]:Hosting补充之配置介绍

ASP.NET Core 运行原理解剖[3]:Middleware-请求管道的构成

IHttpContextFactory

在第一章中,我们介绍到,WebHost 在启动 IServer 时,会传入一个 IHttpApplication<TContext> 类型的对象,Server 负责对请求的监听,在接收到请求时,会调用该对象的 ProcessRequestAsync 方法将请求转交给我们的应用程序。IHttpApplication<TContext> 的默认实现为 HostingApplication ,有如下定义:

public class HostingApplication : IHttpApplication<HostingApplication.Context> {

private readonly RequestDelegate _application;

private readonly IHttpContextFactory _httpContextFactory;

public Context CreateContext(IFeatureCollection contextFeatures)    {        var context = new Context();

var httpContext = _httpContextFactory.Create(contextFeatures);        _diagnostics.BeginRequest(httpContext, ref context);        context.HttpContext = httpContext;

return context;    }

public Task ProcessRequestAsync(Context context)    {

return _application(context.HttpContext);    }

public void DisposeContext(Context context, Exception exception)    {        var httpContext = context.HttpContext;        _diagnostics.RequestEnd(httpContext, exception, context);        _httpContextFactory.Dispose(httpContext);        _diagnostics.ContextDisposed(context);    } }

首先使用 IHttpContextFactory 来创建 HttpContext 实例,然后在 ProcessRequestAsync 方法中调用上一章介绍的 RequestDelegate,由此进入到我们的应用程序当中。

IHttpContextFactory 负责对 HttpContext 的创建和释放,分别对应着CreateDispose方法,它的默认实现类为HttpContextFactory,定义如下:

public class HttpContextFactory : IHttpContextFactory{  

  private readonly IHttpContextAccessor _httpContextAccessor;      private readonly FormOptions _formOptions;  

    public HttpContext Create(IFeatureCollection featureCollection)       {               var httpContext = new DefaultHttpContext(featureCollection);                     if (_httpContextAccessor != null){_httpContextAccessor.HttpContext = httpContext;}  

        var formFeature = new FormFeature(httpContext.Request, _formOptions);featureCollection.Set<IFormFeature>(formFeature);                return httpContext;}    

     public void Dispose(HttpContext httpContext)    {             if (_httpContextAccessor != null){_httpContextAccessor.HttpContext = null;}}
}

如上,HttpContextFactory 只是简单的使用 new DefaultHttpContext(featureCollection) 来创建 HttpContext 的实例,而这里涉及到一个 IFeatureCollection 对象,它是由 Server 根据原始请求创建而来的,下面就先介绍一下该对象。

IFeatureCollection

不过,在介绍 IFeatureCollection 之前,我们先需先回顾一下OWIN:

OWIN是 “Open Web Server Interface for .NET” 的首字母缩写,它定义了一套Web Server和Web Application之间的标准接口,主要用于解除 ASP.NET 与 IIS 的紧密耦合。为此,OWIN 定义了四个核心组件:HostServerMiddlewareApplication,并为Server和Middleware的之间的交互提供了一个 Func<IDictionary<string,object>,Task> 类型的标准接口。

每一个OWIN中间件,都会接收到一个 IDictionary<string,object> 类型的变量,用来表示当前请求的相关信息,也称为环境字典。每一个支持OWIN标准的 Web Server 都会根据请求的原始上下文信息,封装成这个环境字典,然后在OWIN中间件之间传递,进而完成整个请求的处理。环境字典定义了一系列预先约定好的Key,比如:用 "owin.RequestBody" 来表示请求体,"owin.RequestHeaders" 来表示请求头,"owin.RequestMethod" 来表示请求方法等。

OWIN是随着ASP.NET MVC5进行到我们的视线中,在当时,ASP.NET WebAPI 2.0 也基于OWIN实现了自寄宿模式。再后来,提出了 ASP.NET 5 与 MVC6,完全是基于OWIN的模式来开发的,再到今天的 ASP.NET Core,OWIN的概念已被模糊化了,但是还是随处可以见到OWIN的影子,并且也提供了对 OWIN 的扩展支持。

在 ASP.NET Core 中,提出了 IFeatureCollection 的概念,它本质上也是一个 IDictionary<string,object> 键值对,但是它具有面向对象的特点,相对于 IDictionary<string,object> 更加清晰,容易理解,并且Server构建成这样一个对象也很容易,它有如下定义:

public interface IFeatureCollection : IEnumerable<KeyValuePair<Type, object>>
{       bool IsReadOnly { get; }      int Revision { get; }       object this[Type key] { get; set; }TFeature Get<TFeature>();      void Set<TFeature>(TFeature instance);
}

它的定义非常简单,由一系列以键值对来表示的标准特性对象(TFeature)组成,可以通过一个索引以及 Get 和 Set 方法来获取或设置这些特性对象。

下面,我们看一下在 ASP.NET Core 中的对它的一个模拟实现:

public class FeatureCollection : IFeatureCollection{   

 private IDictionary<Type, object> _features;  

   private readonly IFeatureCollection _defaults;  

     private volatile int _containerRevision;   

      public virtual int Revision{              get { return _containerRevision + (_defaults?.Revision ?? 0); }}   

     public object this[Type key]{             get{                   object result;                    return _features != null && _features.TryGetValue(key, out result) ? result : _defaults?[key];}            set{                   if (value == null){                            if (_features != null && _features.Remove(key)){_containerRevision++;}                              return;}                    if (_features == null){_features = new Dictionary<Type, object>();}_features[key] = value;_containerRevision++;}}   

     public TFeature Get<TFeature>(){             return (TFeature)this[typeof(TFeature)];}    

    public void Set<TFeature>(TFeature instance){        this[typeof(TFeature)] = instance;}
}

如上,它的内部属性 _features 便是OWIN中的标准环境字典,并且提供了更加方便的泛型 GetSet 方法,以及一个索引器来访问该环境字典。不过,如果只是这样,那使用起来依然不够方便,更为重要的是 ASP.NET Core 还提供了一系列的特性对象,并以这些特性对象的类型做为环境字典中的Key。

通过上面代码,还可以发现,每次对该环境字典的修改,都会使 Revision 属性递增1。

这里为什么说FeatureCollection是一个模拟的实现呢?具我观察,FeatureCollection对象只在ASP.NET Core的测试代码中用到,而每个Server都有它自己的方式来构建IFeatureCollection,并不会使用FeatureCollection,关于Server中是如何创建IFeatureCollection实例的,可以参考KestrelHttpServer中的实现,这里就不再深究。

那特性对象又是什么呢?我们先看一下请求特性的定义:

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; }   

        string RawTarget { get; set; }IHeaderDictionary Headers { get; set; }Stream Body { get; set; }
}

再看一下表单特性的定义:

public interface IFormFeature{        bool HasFormContentType { get; }IFormCollection Form { get; set; }     IFormCollection ReadForm();    Task<IFormCollection> ReadFormAsync(CancellationToken cancellationToken);
}

可以看到,这些特性对象与我们熟悉的 HttpContext 中的属性非常相似,这也就大大简化了在 IHttpRequestFeature 和 HttpContext 之间的转换。我们可以通过这些特性接口定义的属性来获取到原始上下文中描述的信息,并通过特性对象提供的方法来操作原始上下文,它就像Web Server与我们的应用程序之间的桥梁,完成抽象和具体之间的转换。

ASP.NET Core 提供了一系列丰富的特性对象,如 Session, Cookies, Query, Form, WebSocket, Request, Response 等等, 更详细的列表可以查看 Microsoft.AspNetCore.Http.Features。

HttpContext

HttpContext 对象我们应该都很熟悉了,它用来表示一个抽象的HTTP上下文,而HttpContext对象的核心又体现在用于描述请求的Request和描述响应的Response属性上。除此之外,它还包含一些与当前请求相关的其他上下文信息,如描述当前HTTP连接的ConnectionInfo对象,控制WebSocket的WebSocketManager,代表当前用户的ClaimsPrincipal对象的Session,等等:

public abstract class HttpContext{   

 public abstract IFeatureCollection Features { get; }          public abstract HttpRequest Request { get; }         public abstract HttpResponse Response { get; }        public abstract ConnectionInfo Connection { get; }        public abstract WebSocketManager WebSockets { get; }          public abstract ClaimsPrincipal User { get; set; }         public abstract IDictionary<object, object> Items { get; set; }          public abstract IServiceProvider RequestServices { get; set; }         public abstract CancellationToken RequestAborted { get; set; }           public abstract string TraceIdentifier { get; set; }          public abstract ISession Session { get; set; }         public abstract void Abort();
}

在我们处理请求时,如果希望终止该请求,可以通过 RequestAborted 属性给请求管道发送一个终止信息。当需要对整个管道共享一些与当前上下文相关的数据,可以将它保存在 Items 字典中。而在 ASP.NET Coer 1.x 中还包含一个管理认证的AuthenticationManager对象,但是在 2.0 中,将它移到了 AuthenticationHttpContextExtensions 中,因为用户认证本来就一个相对复杂且独立的模块,把它独立出去会更加符合 ASP.NET Core 的简洁模块化特性。

在上文中,我们了解到 HttpContext 的默认实现使用的是 DefaultHttpContext 类型 ,而 DefaultHttpContext 便是对上面介绍的 IFeatureCollection 对象的封装:

public class DefaultHttpContext : HttpContext{    private FeatureReferences<FeatureInterfaces> _features;    private HttpRequest _request;    private HttpResponse _response;    public DefaultHttpContext(IFeatureCollection features)    {Initialize(features);}    public virtual void Initialize(IFeatureCollection features)    {_features = new FeatureReferences<FeatureInterfaces>(features);_request = InitializeHttpRequest();_response = InitializeHttpResponse();}    protected virtual HttpRequest InitializeHttpRequest() => new DefaultHttpRequest(this);
}

如上,DefaultHttpContext通过 Initialize 来完成从 IFeatureCollection 到 HttpContext 的转换,而各个属性的转换又交给了它们自己。

HttpRequest

HttpRequest 可以用来获取到描述当前请求的各种相关信息,比如请求的协议(HTTP或者HTTPS)、HTTP方法、地址,以及该请求的请求头,请求体等:

public abstract class HttpRequest{       public abstract HttpContext HttpContext { get; }     public abstract string Method { get; set; }      public abstract string Scheme { get; set; }       public abstract bool IsHttps { get; set; }       public abstract HostString Host { get; set; }     public abstract PathString PathBase { get; set; }      public abstract PathString Path { get; set; }     public abstract QueryString QueryString { get; set; }    public abstract IQueryCollection Query { get; set; }      public abstract string Protocol { get; set; }      public abstract IHeaderDictionary Headers { get; }     public abstract IRequestCookieCollection Cookies { get; set; }     public abstract long? ContentLength { get; set; }      public abstract string ContentType { get; set; }     public abstract Stream Body { get; set; }     public abstract bool HasFormContentType { get; }     public abstract IFormCollection Form { get; set; }      public abstract Task<IFormCollection> ReadFormAsync(CancellationToken cancellationToken = new CancellationToken());
}

HttpRequest是一个抽象类,它的默认实现是DefaultHttpRequest:

public class DefaultHttpRequest : HttpRequest{    

private readonly static Func<IFeatureCollection, IHttpRequestFeature> _nullRequestFeature = f => null;    

private FeatureReferences<FeatureInterfaces> _features;      public DefaultHttpRequest(HttpContext context)    {Initialize(context);}   

   public virtual void Initialize(HttpContext context)    {_context = context;_features = new FeatureReferences<FeatureInterfaces>(context.Features);}   

    private IHttpRequestFeature HttpRequestFeature => _features.Fetch(ref _features.Cache.Request, _nullRequestFeature);   

    public override string Method{            get { return HttpRequestFeature.Method; }             set { HttpRequestFeature.Method = value; }} 

    public override Task<IFormCollection> ReadFormAsync(CancellationToken cancellationToken)    {            return FormFeature.ReadFormAsync(cancellationToken);}
}

在 DefaultHttpRequest 中,并没有额外的功能,它只是简单的与 IHttpRequestFeature 中的同名属性和方法做了一个映射,而 IHttpRequestFeature 对象的获取又涉及到一个 FeatureReferences<FeatureInterfaces> 类型, 从字面意思来说,就是对Feature对象的一个引用,用来保存对应的Feature实例,并在上文介绍的 Revision 属性发生变化时,清空Feature实例的缓存:

public struct FeatureReferences<TCache>
{    

public IFeatureCollection Collection { get; private set; }   

 public int Revision { get; private set; }  

   public TCache Cache;[MethodImpl(MethodImplOptions.AggressiveInlining)]       public TFeature Fetch<TFeature, TState>(ref TFeature cached, TState state, Func<TState, TFeature> factory) where TFeature : class{              var flush = false;            var revision = Collection.Revision;          if (Revision != revision){cached = null;flush = true;}               return cached ?? UpdateCached(ref cached, state, factory, revision, flush);}  

  private TFeature UpdateCached<TFeature, TState>(ref TFeature cached, TState state, Func<TState, TFeature> factory, int revision, bool flush) where TFeature : class{        if (flush){Cache = default(TCache);}cached = Collection.Get<TFeature>();          if (cached == null){cached = factory(state);Collection.Set(cached);Revision = Collection.Revision;}             else if (flush)           {Revision = revision;}              return cached;}   

    public TFeature Fetch<TFeature>(ref TFeature cached, Func<IFeatureCollection, TFeature> factory)          where TFeature : class => Fetch(ref cached, Collection, factory);
}

如上,当 Revision 生成变化时,会将 Cache 设置为 null , 然后重新从 IFeatureCollection 中获取,最后更新 Revision 为最新版本,相当于一个缓存工厂。

Fetch方法使用了[MethodImpl(MethodImplOptions.AggressiveInlining)]特性,表示该方法会尽可能的使用内联方式来执行。而内联是一种很重要的优化方式, 它允许编译器在方法调用开销比方法本身更大的情况下消除对方法调用的开销,即直接将该方法体嵌入到调用者中。

HttpResponse

在了解了表示请求的抽象类 HttpRequest 之后,我们再来认识一下与它对应的,用来描述响应的 HttpResponse 类型:

public abstract class HttpResponse{   

 private static readonly Func<object, Task> _callbackDelegate = callback => ((Func<Task>)callback)();  

   private static readonly Func<object, Task> _disposeDelegate = disposable =>{((IDisposable)disposable).Dispose();           return Task.CompletedTask;};  

     public abstract HttpContext HttpContext { get; }   

     public abstract int StatusCode { get; set; }   

     public abstract IHeaderDictionary Headers { get; } 

     public abstract Stream Body { get; set; }  

     public abstract long? ContentLength { get; set; }  

     public abstract string ContentType { get; set; }   

     public abstract IResponseCookies Cookies { get; }  

     public abstract bool HasStarted { get; }   

     public abstract void OnStarting(Func<object, Task> callback, object state);   

     public virtual void OnStarting(Func<Task> callback) => OnStarting(_callbackDelegate, callback);  

     public abstract void OnCompleted(Func<object, Task> callback, object state);   

     public virtual void RegisterForDispose(IDisposable disposable) => OnCompleted(_disposeDelegate, disposable);  

     public virtual void OnCompleted(Func<Task> callback) => OnCompleted(_callbackDelegate, callback);   

     public virtual void Redirect(string location) => Redirect(location, permanent: false);  

     public abstract void Redirect(string location, bool permanent);
}

HttpResponse也是一个抽象类,我们使用它来输出对请求的响应,如设置HTTP状态码,Cookies,HTTP响应报文头,响应主体等,以及提供了一些将响应发送到客户端时的相关事件。

其 HasStarted 属性用来表示响应是否已开始发往客户端,在我们第一次调用 response.Body.WriteAsync 方法时,该属性便会被设置为 True。需要注意的是,一旦 HasStarted 设置为 true 后,便不能再修改响应头,否则将会抛出 InvalidOperationException 异常,也建议我们在HasStarted设置为true后,不要再对 Response 进行写入,因为此时 content-length 的值已经确定,继续写入可能会造成协议冲突。

HttpResponse 的默认实现为 DefaultHttpResponse ,它与 DefaultHttpRequest 类似,只是对 IHttpResponseFeature 的封装,不过 ASP.NET Core 也为我们提供了一些扩展方法,如:我们在写入响应时,通常使用的是 Response 的扩展方法 WriteAsync :

public static class HttpResponseWritingExtensions{  

  public static Task WriteAsync(this HttpResponse response, string text, CancellationToken cancellationToken = default(CancellationToken))    {        return response.WriteAsync(text, Encoding.UTF8, cancellationToken);}   

  public static Task WriteAsync(this HttpResponse response, string text, Encoding encoding, CancellationToken cancellationToken = default(CancellationToken))    {          byte[] data = encoding.GetBytes(text);         return response.Body.WriteAsync(data, 0, data.Length, cancellationToken);}
}

ASP.NET Core 还为 Response 提供了用来一个清空响应头和响应体的扩展方法:

public static class ResponseExtensions{    

public static void Clear(this HttpResponse response)    {        if (response.HasStarted){                 throw new InvalidOperationException("The response cannot be cleared, it has already started sending.");}response.StatusCode = 200;response.HttpContext.Features.Get<IHttpResponseFeature>().ReasonPhrase = null;response.Headers.Clear();             if (response.Body.CanSeek){response.Body.SetLength(0);}}
}

还有比较常用的发送文件的扩展方法:SendFileAsync ,获取响应头的扩展方法:GetTypedHeaders 等等,就不再细说。

IHttpContextAccessor

在 ASP.NET 4.x 我们经常会通过 HttpContext.Current 来获取当前请求的 HttpContext 对象,而在 ASP.NET Core 中,HttpContext 不再有 Current 属性,并且在 ASP.NET Core 中一切皆注入,更加推荐使用注入的方式来获取实例,而非使用静态变量。因此,ASP.NET Core 提供了一个 IHttpContextAccessor接口,用来统一获取当前请求的 HttpContext 实例的方式:

public interface IHttpContextAccessor{HttpContext HttpContext { get; set; }
}

它的定义非常简单,就只有一个 HttpContext 属性,它在ASP.NET Core 中还有一个内置的实现类:HttpContextAccessor

public class HttpContextAccessor : IHttpContextAccessor{    

private static AsyncLocal<HttpContext> _httpContextCurrent = new AsyncLocal<HttpContext>();  

  public HttpContext HttpContext{               get{               return _httpContextCurrent.Value;}              set{_httpContextCurrent.Value = value;}}
}

这里使用了一个 AsyncLocal<T> 类型来保存 HttpContext 对象,可能很多人对 AsyncLocal 不太了解,这里就来介绍一下:

在.NET 4.5 中引用了 async await 等关键字,使我们可以像编写同步方法一样方便的来执行异步操作,因此我们的大部分代码都会使用异步。以往我们所使用的 ThreadLocal 在同步方法中没有问题,但是在 await 后有可能会创建新实的例(await 之后可能还交给之前的线程执行,也有可能是一个新的线程来执行),而不再适合用来保存线程内的唯一实例,因此在 .NET 4.6 中引用了 AsyncLocal<T> 类型,它类似于 ThreadLocal,但是在 await 之后就算切换线程也仍然可以保持同一实例。我们知道在 ASP.NET 4.x 中,HttpContext的 Current 实例是通过 CallContext 对象来保存的,但是 ASP.NET Core 中不再支持CallContext,故使用 AsyncLocal<T> 来保证线程内的唯一实例。

不过,ASP.NET Core 默认并没有注入 IHttpContextAccessor 对象,如果我们想在应用程序中使用它,则需要手动来注册:

public void ConfigureServices(IServiceCollection services){ervices.AddSingleton<IHttpContextAccessor, HttpContextAccessor>();
}

在上面介绍的HttpContextFactory类的构造函数中会注入IHttpContextAccessor实例,并为其HttpContext属性赋值,并在Dispose方法中将其设置为null。

总结

在ASP.NET 4.x 中,我们就对 HttpContext 非常熟悉了,而在 ASP.NET Core 中,它的变化并不大,只是做了一些简化,因此本文较为简单,主要描述了一下 HttpContext 是如何创建的,以及它的构成,最后则介绍了一下在每个请求中获取 HttpContext 唯一实例的方式,而在 ASP.NET Core 2.0 中 HttpContext 的 AuthenticationManager 对象已标记为过时,添加了一些扩展方法来实现AuthenticationManager中的功能,下一章就来介绍一下 ASP.NET Core 中的认证系统。

相关文章:

  • .NET Core 2.0 正式发布信息汇总

  • .NET Standard 2.0 特性介绍和使用指南

  • .NET Core 2.0 的dll实时更新、https、依赖包变更问题及解决

  • .NET Core 2.0 特性介绍和使用指南

  • Entity Framework Core 2.0 新特性

  • 体验 PHP under .NET Core

  • .NET Core 2.0使用NLog

  • 升级项目到.NET Core 2.0,在Linux上安装Docker,并成功部署

  • 解决Visual Studio For Mac Restore失败的问题

  • ASP.NET Core 2.0 特性介绍和使用指南

  • .Net Core下通过Proxy 模式 使用 WCF

  • .NET Core 2.0 开源Office组件 NPOI

  • ASP.NET Core Razor页面 vs MVC

  • Razor Page–Asp.Net Core 2.0新功能  Razor Page介绍

  • MySql 使用 EF Core 2.0 CodeFirst、DbFirst、数据库迁移(Migration)介绍及示例

  • .NET Core 2.0迁移技巧之web.config配置文件

  • asp.net core MVC 过滤器之ExceptionFilter过滤器(一)

  • ASP.NET Core 使用Cookie验证身份

  • ASP.NET Core MVC – Tag Helpers 介绍

  • ASP.NET Core MVC – Caching Tag Helpers

  • ASP.NET Core MVC – Form Tag Helpers

  • ASP.NET Core MVC – 自定义 Tag Helpers

  • ASP.NET Core MVC – Tag Helper 组件

  • ASP.NET Core 运行原理解剖[1]:Hosting

  • ASP.NET Core 运行原理解剖[2]:Hosting补充之配置介绍

  • ASP.NET Core 运行原理解剖[3]:Middleware-请求管道的构成

原文地址:http://www.cnblogs.com/RainingNight/p/httpcontext-in-asp-net-core.html


.NET社区新闻,深度好文,微信中搜索dotNET跨平台或扫描二维码关注

ASP.NET Core 运行原理解剖[4]:进入HttpContext的世界相关推荐

  1. ASP.NET Core 运行原理解剖[5]:Authentication

    在现代应用程序中,认证已不再是简单的将用户凭证保存在浏览器中,而要适应多种场景,如App,WebAPI,第三方登录等等.在 ASP.NET 4.x 时代的Windows认证和Forms认证已无法满足现 ...

  2. ASP.NET Core 运行原理解剖[1]:Hosting

    SP.NET Core 是新一代的 ASP.NET,第一次出现时代号为 ASP.NET vNext,后来命名为ASP.NET 5,随着它的完善与成熟,最终命名为 ASP.NET Core,表明它不是 ...

  3. ASP.NET Core 运行原理解剖[3]:Middleware-请求管道的构成

    在 ASP.NET 中,我们知道,它有一个面向切面的请求管道,有19个主要的事件构成,能够让我们进行灵活的扩展.通常是在 web.config 中通过注册 HttpModule 来实现对请求管道事件监 ...

  4. ASP.NET Core 运行原理解剖[2]:Hosting补充之配置介绍

    在上一章ASP.NET Core 运行原理解剖[1]:Hosting中,我们介绍了 ASP.NET Core 的启动过程,主要是对 WebHost 源码的探索.而本文则是对上文的一个补充,更加偏向于实 ...

  5. ASP.NET Core 运行原理剖析2:Startup 和 Middleware(中间件)

    在上一节(ASP.NET Core 运行原理剖析1:初始化WebApp模版并运行)中提到ASP.NET Core WebApp 必须含有Startup类,在本节中将重点讲解Startup类以及Midd ...

  6. ASP.NET Core 运行原理剖析1:初始化WebApp模版并运行

    之前两篇文章简析.NET Core 以及与 .NET Framework的关系和.NET Core的构成体系从总体上介绍.NET Core,接下来计划用一个系列对ASP.NET Core的运行原理进行 ...

  7. ASP.NET Core 运行原理剖析

    1.1. 概述 在ASP.NET Core之前,ASP.NET Framework应用程序由IIS加载.Web应用程序的入口点由InetMgr.exe创建并调用托管.以初始化过程中触发HttpAppl ...

  8. ASP.NET Core 运行原理

    1.1. 概述 在ASP.NET Core之前,ASP.NET Framework应用程序由IIS加载.Web应用程序的入口点由InetMgr.exe创建并调用托管.以初始化过程中触发HttpAppl ...

  9. iis6.0与asp.net的运行原理

    这几天上网翻阅了不少前辈们的关于iis和asp.net运行原理的博客,学的有点零零散散,花了好长时间做了一个小结(虽然文字不多,但也花了不少时间呢),鄙人不才,难免有理解不道的地方,还望前辈们不吝赐教 ...

最新文章

  1. 福利 | 零基础学习Python量化交易 !(深圳)
  2. 【CVPR2020】可微分的NAS方法汇总
  3. python中文什么意思-python中class是什么意思
  4. python max函数_使用'key'和lambda表达式的python max函数
  5. 系统架构设计师 - 第三方认证服务
  6. stm32高级定时器 基础知识
  7. chrome使用脚本修改组策略_允许Chrome安装第三方网站的脚本以及扩展的方法
  8. 视频教程-汇编语言程序设计IV-其他
  9. 【网络攻防原理与技术】第6章:特洛伊木马
  10. 极大似然函数求解_快速理解极大似然法
  11. 十大进销存管理软件亮点大对比
  12. mongodb mongoose 的使用
  13. 视频信息和信号的特点
  14. 运筹说第38期“迟到“的毕业证-趣闻轶事(三)
  15. 面向初学者的 Python IDE:Thonny,你值得一试
  16. 微信小程序游戏开发介绍,微信互动游戏有哪些?
  17. 输出数组中满足条件元素的坐标
  18. 2.6-2.7 向量运算的基本性质零向量
  19. 【Shell简单练习2】Shell 脚本输出指定文件夹下的文件名到指定的文件中
  20. web自动化测试笔记

热门文章

  1. XHTML教会我的一些东西-1
  2. li或dd 浮动后增加图片时高度多出3-5px的问题
  3. 基于centos5.8源码安装nginx之LNMP
  4. QT-Linux开发环境的搭建
  5. 引号(反引号、$()符号)
  6. 使用 dotnet-monitor 在 Kubernetes 中收集 .NET metrics
  7. .NET Conf 2021 正在进行中,带你看一看微软带来了什么内容
  8. WPF 如何流畅地滚动ScrollViewer
  9. .net core 下的分布式事务锁
  10. 前端老弟第一次写后端,崩了!