本鱼拟成立工作室承接项目开发/软件定制/云设施开发运维/办公设备技术支持等,如您有相关需求,欢迎来询 | ::博客文章推荐::

FSLIB.NETWORK手册(1) · 基本概念和流程

: DOT.NET 木魚 10998℃ 5评论
FSLIB.NETWORK 是什么?它是一个为HTTP协议设计的高度简便易用且极具功能性的类库。它的由来要追溯到12306订票助手.NET的诞生。在12306订票助手.NET诞生之初,我就有这么一个需求,需要一个用起来十分简单易用且灵活性和强健于一身的网络请求库。

为什么有这些要求呢?因为我懒,所以需要这个类库十分灵活简便易用,最好只要告诉它我有什么然后要什么,它就能给我弄回来。
然而12306的种种掉链子谁都知道,所以我也希望这个库足够强健,至少不会因为库本身的因素导致程序崩掉。

所以这个库的存在是十分重要的。然而幸运的是,这个库完善到现在后,基本上符合我的要求。

提示:如果您在此网络库的使用过程中遇到任何问题,请在 问答社区(http://ask.fishlee.net/category-21) 提问。博客并不是提问的好地方。

第一节,概念和流程概述

FSLIB.NETWORK(以下简称网络库)从设计的一开始,就借鉴了 .NET4.5+ 中提供的 HttpClient 的设计结构,并进行了抽象和流程化,为的是简便易用。

从整个流程看,网络库将整个 HTTP 请求中涉及到的核心对象分为以下几类:

类型 网络库内部实现 功能描述
HTTP会话工厂 HttpClient HTTP网络库核心类,用于创建会话并维护全局设置
HTTP会话 HttpContext, HttpContext<T> HTTP单个请求的封装,每个对象完成一次具体的请求,由HTTP会话工厂创建
HTTP请求信息 HttpRequestMessage 维护了所有的请求信息,如请求头信息、正文信息、相关设置等等
HTTP请求数据 HttpRequestContent 以及它的派生类 如果发送的请求带有正文,则正文为此类型数据。此类型的实例作为HTTP请求正文放在 HttpRequestMessage 中
HTTP响应信息 HttpResponseMessage 维护了所有的响应信息,如响应头信息、响应数据等等
HTTP响应数据 HttpResponseContent 以及它的派生类 包含了服务器返回的最终结果
HTTP请求工厂 IHttpHandler, HttpHandler 用于创建需要的各个细节对象,如 HttpWebRequest。此类被HttpClient对象使用
HTTP设置 HttpSettings 维护了全局的设置

HTTP请求是流程化的,其基本运行流程如下图所示

http_network_lib_work_flow

具体描述如下

  1. 客户端要求发送请求,创建客户端HttpClient对象实例,并设置相关设置
  2. 调用HttpClient对象的Create方法(或相关的简便写法),传递必须的参数(方法类型、URL、引用页、地址、头信息以及一部分常用选项设置等)以及期望的响应类型(如Stringbyte[]等),HttpContext对象将会调用HttpHandler依据相关的信息进行初始化并创建需要的HttpContext。在此过程中,HttpClient会将传递的参数进行包装。此时,HttpContext尚未发送
  3. 根据需要,对HttpContext进行设置(可选)
  4. 调用HttpContext对象的Send方法(同步)或SendAsync(异步)方法发送请求
  5. HttpContext对象调用HttpHandler新建HttpWebRequest对象,并进行初始化
  6. HttpContext开始请求,连接服务器并等待请求流
  7. 写入请求头。如果不出错,则判断是否有请求数据,如果没有,则跳到第 9 步骤
  8. 向流写入请求数据
  9. 等待响应头。获得响应头后,判断是否正确,并更新Cookies。如果此时发生ProtocolException(常见于?40x/50x?错误),则会进行二次处理以便于获得响应内容
  10. 校验响应头是否正常。如果正常,则对响应流进行包装(包含但不局限于镜像、解压缩等)
  11. 将响应流交由HttpResponseMessage进行处理,它负责将流数据读取并处理完成
  12. 校验响应数据是否正确,并记录错误
  13. 请求完成

在整个流程中,只有第一步到第四步是每次都需要完成的操作,其它的操作一般来说,并不需要调用方过多关心。

第二节,发送数据处理模型

对于HTTP应用来说,最常见的莫过于发送和处理响应数据。本网络库为了简化数据发送流程,对发送数据内容进行了抽象化,将所有的要发送的数据统一为HttpRequestContent并由其完成发送的写入过程。

在调用HttpClient创建HttpContext的过程中,HttpClient会依据一定的规则将传入的数据进行包装以便于发送。最终请求发送的数据格式由两个参数决定:ContentType和实际的数据 data。其中,ContentType 决定了由哪个数据承载类对data进行处理并发送。

HttpClient原始数据(参数传入的数据)转换为HttpRequestContent的流程,可以用下图表示。

http_network_lib_format_data_work_flow

在大部分情况下,HttpClient内建的自动处理机制已经可以处理绝大部分数据类型。其默认的处理机制如下表格所示(按列表顺序处理,data为传入的数据,ContentType为指定的类型)。

情况 返回类型 备注
data 类型可转换为 HttpRequestContent data 直接强制类型转换为 HttpRequestContent 后返回  
data 类型为 string 包装为 RequestStringContent 原样发送
data 类型为 Stream 包装为 RequestCopyStreamContent 直接复制流并直接发送
data 类型为 字节数组 byte[] 包装为 RequestByteBufferContent 直接写入数组
data 为字典类型(IDictionary<string, string>
且 ContentType 不为 ContenType.Json
包装为 RequestFormDataContent 生成表单数据
ContentType 为 ContentType.Json 包装为 RequestJsonContent 使用 Json.Net 库对数据进行序列化
ContentType 为 ContentType.Xml 包装为 RequestXmlContent 使用XML序列化对数据进行序列化
(默认) 包装为 RequestObjectContent 内部使用反射机制对data进行处理

值得一提的是 RequestObjectContent,它继承自 RequestFormData,提供了一个快速方便的方式将各种可能的数据类型格式化为表单,并支持文件上传。这个类型内部使用反射机制,对传入的数据类型进行二次绑定。

RequestObjectContent中,其将 data 作为顶级对象进行绑定,存在一个递归过程,处理逻辑如下:

http_network_lib_roc_bindflow

RequestObjectContent 处理完成后,由底层的 RequestFormContent 负责将实际的数据写入请求流中。以上绑定的时机位于HttpContext的发送过程中。

提示:在处理流程中,有个涉及到 IContentPayloadBuilder 的接口也参与其中,而上面并没有涉及到。后续的高级用法中,如果需要的话,将会对其做介绍。

第三节,接收数据处理模型

在创建HttpContext的时候,就已经要求指定返回结果的类型,在Create方法中,HttpClient对象会依据期望的数据类型结果来创建HttpContext的泛型类型。在这之后,通过HttpContextResult属性即可快速拿到相关结果。

提示:当发生错误,或服务器返回了错误的结果(如4xx、5xx)结果时,HttpContext会依据实际返回的结果动态调整响应类型。此时,如果继续使用Result属性取结果,将有可能导致抛出异常(类型不匹配)。因此,当请求不成功(IsValid() 返回false)时,应该使用 HttpContextResponseContent 获得响应结果,并需要根据情况判断其类型。

网络库内置了如下类型结果的支持

  • byte[]:直接获得原始的响应字节数组
  • Stream:获得原始的响应流并进行复制。此时,响应结果为 ResponseCopyStreamContent,它的 Stream 属性表示了镜像流。此镜像流可以通过 Create 时的 targetStream 参数传入,或使用自动生成的 MemoryStream。所有的服务器响应将会以同步或异步的模式写入这个流中,并在响应关闭后可以继续使用
  • EventHandler<ResponseStreamContent.RequireProcessStreamEventArgs>:一个特殊的类型,以事件模式处理响应。此时,响应结果为 ResponseStreamContent,它的 RequireProcessStream 事件表示已经读取到数据并等待处理。此事件可以通过Create时的streamInvoker参数传入,或在发送之前手动绑定
  • XmlDocument:返回一个XML文档结果
  • Image:返回一个图像结果
  • 其它的非 object 类型:返回 ResponseObjectContent,此类型自动判断响应,并尝试通过JSON或XML反序列化将响应反序列化为对象
  • 其它情况:返回 ResponseBinaryContent,此类型和byte[]类型基本一致

通过额外的HtmlAgility扩展包可提供对 HtmlDocument 的返回支持。

 

喜欢 (27)
发表我的评论
取消评论
表情

Hi,您需要填写昵称和邮箱!

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址
(5)个小伙伴在吐槽
  1. 图挂了

    秋天2021-09-14 09:09 回复
  2. 好漂亮的流程图,想问一下这是用啥画的~

    zy08512016-05-23 09:07 回复
    • wordpress里的一个插件,是个在线画图保存SVG的工具。Chrome也有类似的应用。

      木魚2016-05-23 11:21 回复
  3. 等这个手册等的真不容易。。。。。

    Demo2016-05-06 10:52 回复
  4. 大赞啊,这个图得耗费你不少心思吧

    尼采我思谁2016-05-06 09:58 回复