我们介绍过了浏览器和服务器之间的交互过程,接下来介绍Asp.net处理动态请求。

写自己的Asp.Net框架,我们不会引用System.Web这个程序集,我们只需要创建要给自己的类库,所以在接下来的程序中,我们所用到的Web组件都是我们自己定义的。

首先创建一个程序集名为MyWebApplication,定义了如下一个HttpContext类型,它封装了上下文对象。

一、HttpContext定义了三个属性:

表示当前服务器请求——HttpRequest

服务器响应——HttpResponse

一个"工具类"——HttpServerUtility

public class HttpContext
    {
        public HttpContext(string strRequest);
 
        public HttpRequest Request { get; }
        public HttpResponse Response { get; }
        public HttpServerUtility Server { get; set; }
    }

与真实的底层有些差别,真实的底层,上下文封装比较繁琐,当然它足够强大。

在这里,我们就简单的讲请求报文进行分类,把分类的数据结果交给HttpRequest来保管:

HttpRequest的构造函数,接受一个请求报文,对请求报文进行截取分类,

我们在这里就只挑拣两个必须的用到的数据url和httpMethod。

NameValueCollection是一个字典类型的集合,它对Get请求的数据进行了封装,以供我们可以根据不同的参数获取不同的响应数据  Request.QueryString[]嘛,很熟悉吧。

public class HttpRequest
    {
        public HttpRequest(string strRequest);
 
        public string HttpMethod { get; set; }
        public NameValueCollection QueryString { get; set; }
        public string Url { get; set; }
    }

HttpResponse里提供了Write方法,最后输出都要转换为byte字节的,Write(stringresponseStr)的方法是对输出的文本进行累加,输出的时候统一转为了byte字节。 CreateResponseContent这个方法就是在输出的时候对响应报文(ResponseBody和ResponseHead)进行了整合。

public class HttpResponse
    {
        public HttpResponse(HttpRequest request);
 
        public HttpRequest Request { get; set; }
        public byte[] ResponseBody { get; }
        public byte[] ResponseHeader { get; set; }
 
        public void CreateResponseContent();
        public void Write(byte[] b);
        public void Write(string responseStr);
    }

HttpServerUtility就提供了一个方法,为了替换Html模板,对虚拟路径与程序集下的物理路径进行了映射:

public class HttpServerUtility : MarshalByRefObject
    {
        public HttpServerUtility();
 
        public string MapPath(string path);
    }

二、HttpApplication对象

这个对象是构成Asp.Net管道的核心对象,实际的HttpApplication是通过反射的方式来创建的,而且此方法要抽象很多,我们在下一篇文章会介绍实际的HttpApplication是怎么回事。

我们的HttpApplication里面定义了19个事件,HttpModule就是对这些事件进行注册。

InitInternal方法是HttpApplication的初始化方法,它在ProcessRequest方法里会被执行;InitInteranl方法中,调用了一个InitModules方法初始化所有的HttpModele。

ProcessRequest方法是绑定并开始执行所有HttpModule里注册的事件,并在第7-8个事件根据请求的url路径,

通过反射的方式创建了HttpHandler,在第11-12个时间中执行了HttpHandler的ProcessRequest方法。

public class HttpApplication : IHttpHandler
    {
        public HttpApplication();
 
        public HttpContext Context { get; set; }
 
        public event EventHandler AcquireRequestState;
        public event EventHandler AunthorizeRequest;
        public event EventHandler AuthenticateRequest;
        public event EventHandler BeginRequest;
        public event EventHandler EndRequest;
        public event EventHandler LogRequest;
        public event EventHandler PostAcquireRequestState;
        public event EventHandler PostAuthenticateRequest;
        public event EventHandler PostAuthorizeRequest;
        public event EventHandler PostLogRequest;
        public event EventHandler PostMapRequestHandler;
        public event EventHandler PostReleaseRequestState;
        public event EventHandler PostRequestHandlerExecute;
        public event EventHandler PostResolveRequestCache;
        public event EventHandler PostUpdateRequestCache;
        public event EventHandler PreRequestHandlerExecute;
        public event EventHandler ReleaseRequestState;
        public event EventHandler ResolveRequestCache;
        public event EventHandler UpdateRequestCache;

private void InitModules();

public void InitInternal();
        public void ProcessRequest(HttpContext context);
    }

HttpModule:

HttpApplication里的事件是按顺序执行的,HttpContext就是从HttpApplication的开始  "流" 到结束,这过程中就是这一些列事件来对HttpContext的请求进行处理的,有些事件在到达真正的处理中心(一般处理程序处理)之前就可以执行,或之后执行。比如:AuthenticateRequest是验证请求,用在获取用户信息的时候;PostAcquireRequestState这里以获取到了Session,这这个事件之后才能获取的session。

所以我们可以在不同的时间注册不同的事件来更好的扩展HttpApplication的请求处理.

在这里没有用配置文件去指定HttpModule,而是指定了一个HttpModules的文件夹,遍历这个文件夹下所有的cs文件,通过反射的方式创建成IHttpModule,执行其Init方法

private void InitModules()
        {
            HttpModuleCollection moduleCollection = new HttpModuleCollection();
            if (Directory.Exists("Modules"))
            {
                string[] modulesPath = Directory.GetFiles("Modules");
                foreach (string item in modulesPath)
                {
                    if (File.Exists(item))
                    {
                        if (Path.GetExtension(item) == ".cs")
                        {
                            string moduleName = Assembly.GetEntryAssembly().GetName().Name
                                       + ".Modules." + Path.GetFileNameWithoutExtension(item);
                            IHttpModule module = Assembly.GetEntryAssembly()

.CreateInstance(moduleName) as IHttpModule;
                            moduleCollection.AddModule(moduleName, module);
                            module.Init(this);
                        }
                    }
                }
            }
        }

在IHttpModule里面,就只有一个Init方法,这个方法通过HttpApplication类型,可以在这里注册HttpApplication的事件响应方法:

public interface IHttpModule
    {
        void Init(HttpApplication context);
    }

IHttpHandler:这个就和我们的一般处理程序一样了,我们在一个类里继承这个接口,实现此ProcessRequest方法。

HttpHandler是Http请求真正的处理中心,在HttpApplication的事件管道中被创建执行。

public interface IHttpHandler
    {
        void ProcessRequest(HttpContext context);
    }

大概就这些了,我们整理一下思路:

当一个http请求到达服务器,服务器通过监听socket请求,获取到http请求报文,HttpContext封装http请求,HttpApplication对象的ProcessRequest方法来处理这个请求,最后封装响应报文,响应给浏览器。

现在,我们来试验一下:

先把我们自己写的Asp.Net的程序集编译成一个dll文件;像上一篇文章一样,创建一个WinForm应用程序,引入我们的MyWebApplication程序集。

监听客户端请求可以这么改一下了

while (true)
                {
                    Socket clientSocket = serverSocket.Accept();
                    byte[] data = new byte[1024 * 1024];
                    int len = clientSocket.Receive(data, 0, data.Length, SocketFlags.None);
                    if (len <= 0)
                    {
                        clientSocket.Shutdown(SocketShutdown.Both);
                        clientSocket.Close();
                        continue;
                    }
                    string strRequest = Encoding.UTF8.GetString(data, 0, len);
                    HttpContext context = new HttpContext(strRequest);            //创建上下文对象
                    if (context.Request.Url.EndsWith(".aspx")           
                        || context.Request.Url.EndsWith(".ashx"))
                    {
                        HttpApplication application = new HttpApplication();             
                        application.ProcessRequest(context);                          //创建Application,并执行PR方法
                    }
                    else                             //如果是静态文件,找到其路径,读取为byte字节放入输出流
                    {
                        string fileData = Path.Combine(AppDomain.CurrentDomain.BaseDirectory + context.Request.Url.TrimStart('/').Replace('/', '\\'));
                        if (!File.Exists(fileData))
                        {
                            context.Response.Write("不存在此路径");
                        }
                        else
                        {
                            context.Response.Write(File.ReadAllBytes(fileData));
                        }
                    }
                    clientSocket.Send(context.Response.ResponseHeader);            
                    clientSocket.Send(context.Response.ResponseBody);
                    clientSocket.Shutdown(SocketShutdown.Both);
                    clientSocket.Close();
                }
            }, socket);

然后,我们创建一个Modules文件夹,用来放扩展HttpModule文件。   --MyHttpModule

创建一个继承自IHttpHandler的处理程序                                            --SimpleHandler

public class TestHandler : IHttpHandler
    {
 
        public void ProcessRequest(HttpContext context)
        {
            string id = context.Request.QueryString["id"];
            string name = context.Request.QueryString["name"];
 
            string path = context.Server.MapPath("Test.html");
            string html = File.ReadAllText(path);
            string replaceStr = string.Format("id:{0}<br />name:{1}", id, name);
            html = html.Replace("@test", replaceStr);
            context.Response.Write(html);
 
            context.Response.Write("<br/>HttpHandler执行完毕!<br/>");
        }
    }

我们也试验一下HttpModule扩展:

public class MyModule : IHttpModule
    {
        public void Init(HttpApplication context)
        {
            context.BeginRequest += context_BeginRequest;
            context.EndRequest += context_EndRequest;
        }

//HttpApplication里最后一个事件

void context_EndRequest(object sender, EventArgs e)
        {
            HttpApplication app = sender as HttpApplication;
            HttpContext context = app.Context;
            context.Response.Write("<br />HttpModule---EndRequest执行了<br />");
        }

//这是HttpApplication里的第一个事件

void context_BeginRequest(object sender, EventArgs e)
        {
            HttpApplication app = sender as HttpApplication;
            HttpContext context = app.Context;
            context.Response.Write("HttpModule---BeginRequest执行了<br />");
        }
    }

启动我们的服务器访问:

http://127.0.0.1:9999/TestHandler.aspx?id=23&name=Test

我们把这个.cs类就作为一个动态请求,后面加aspx或者ashx。

可以看到,我们的HttpModule在这里也生效了:

首选执行了HttpModule里注册的BeginRequest事件

然后输出了HttpHandler里面执行的结果,还有get请求的参数…HttpHandler执行完毕!

最后执行HttpModule里面注册的EndRequest事件

源码下载

[置顶] Asp.Net底层原理(二、写自己的Asp.Net框架)的更多相关文章

  1. [置顶] Asp.Net底层原理(一、浏览器和服务器的交互原理)

    …… 一.浏览器和服务器的交互原理 二.写自己的"迷你"Asp.net框架 三.Asp.Net的请求与响应过程 1.在此之前,首先简单的模拟一下我们去请求一个网址的时候,浏览器和服 ...

  2. iOS 技术篇:从使用到了解block底层原理 (二)

    block实质 序言 上篇文章中主要通过简单的demo展示了block的使用场景,本篇将基于上篇文章iOS 技术篇:从使用到了解block底层原理 (一)进一步了解block底层的实现原理. bloc ...

  3. ASP.NET底层原理

    上图基本上演示了IIS 6整个处理过程.在User Mode下,http.sys接收到一个基于aspx的http request,然后它会根据IIS中的Metabase查看该基于该Request的Ap ...

  4. (转)Asp.Net底层原理(三、Asp.Net请求响应过程)

    原文地址:http://www.cnblogs.com/liuhf939/archive/2013/09/16/3324753.html 在之前,我们写了自己的Asp.Net框架,对整个流程有了一个大 ...

  5. [置顶] 文件和目录(二)--unix环境高级编程读书笔记

    在linux中,文件的相关信息都记录在stat这个结构体中,文件长度是记录在stat的st_size成员中.对于普通文件,其长度可以为0,目录的长度一般为1024的倍数,这与linux文件系统中blo ...

  6. [置顶] SNMP协议详解<二>

    上一篇文章讲解了SNMP的基本架构,本篇文章将重点分析SNMP报文,并对不同版本(SNMPv1.v2c.v3)进行区别! 四.SNMP协议数据单元 在SNMP管理中,管理站(NMS)和代理(Agent ...

  7. [置顶] 程序员必知(二):位图(bitmap)

    位图是什么? 位图就是数组,一般来说是bit型的数组,具有快速定位某个值的功能,这种思想有很广泛的应用,比如下边两题: 1 找出一个不在5TB个整数中存在的数 假设整数是32位的,总共有4GB个数,我 ...

  8. [置顶] Isolation Forest算法原理详解

    本文只介绍原论文中的 Isolation Forest 孤立点检测算法的原理,实际的代码实现详解请参照我的另一篇博客:Isolation Forest算法实现详解. 或者读者可以到我的GitHub上去 ...

  9. [置顶] Jquery学习总结(二) jquery选择器详解

    1.基本选择器 l ID 根据元素ID选择 l Elementname 根据元素名称选择 l Classname 根据元素css类名选择 举例: <input type=”text” id=”I ...

随机推荐

  1. B - Moving Tables

    B - Moving Tables Time Limit:1000MS     Memory Limit:32768KB     64bit IO Format:%I64d & %I64u S ...

  2. Notes常用事件整理

    ①      ボタンのクリック事件: Sub Click(Source As Button) Dim ws As New NotesUIWorkspace Dim uidoc As NotesUIDo ...

  3. zoj 2587 Unique Attack 最小割判定

    题目链接 让你判断最小割是否唯一. 判断方法是, 先求一遍最大流, 然后从源点dfs一次, 搜索未饱和边的数目. 从汇点dfs一次, 同样也是搜索未饱和边的数目, 看总和是否等于n. 如果等于n那么唯 ...

  4. AndroidStudio 使用Hide API

    1.反射法 速度慢 2.生成新的android.jar 通常需要隐藏API的地方并不多 不需要整个都编译 而且编译出的framework.jar也不全 缺少java.*和javax.* 所以只把需要的 ...

  5. 转:angular的decorator方法

    AngularJS实例 – 装饰$log 在AngularJS中,我们可以使用Angular内置或者自定义的services,在应用的各个部分之间分享数据和方法.假设你已经定义了一个service,但 ...

  6. 派生类地址比基类地址少4(子类与基类指针强行转换的时候,值居然会发生变化,不知道Delphi BCB是不是也这样) good

    大家对虚表并不陌生,都知道每个含有虚函数的类对象都有1个虚指针,但是在现实使用中,却总是因为这而调试半天,才发现原来是虚指针惹的祸.我这几天在调试代码时候也中招了,我的问题是这样的,如下图,CTree ...

  7. Eclipse启动后一直Initializing Java Tooling (1%)

    问题症状: 工作中eclipse崩溃,再次启动后cpu占用99%,状态栏一直显示Initializing Java Tooling: (1%). 解决方案: 删除\workspace\.metadat ...

  8. 如何从 0 开始学 ruby on rails (漫步版)

    如何从 0 开始学 ruby on rails (漫步版) ruby 是一门编程语言,ruby on rails 是 ruby 的一个 web 框架,简称 rails. 有很多人对  rails 感兴 ...

  9. CATransition类动画

    - (void)leftClick { [UIView beginAnimations:nil context:nil]; //display mode, slow at beginning and  ...

  10. 一步一步学android之布局管理器——LinearLayout

    线性布局是最基本的一种布局,在基本控件篇幅中用到的都是LinearLayout,线性布局有两种方式,前面也有用到,一种是垂直的(vertical),一种是水平的(horizontal).我们同样来看下 ...