完成OSS.Http底层HttpClient重构封装 支持标准库
OSS.Http项目对于.Net Standard标准库的支持已经迁移完毕,OSS开源系列两个最底层的类库已经具备跨运行时支持的能力。由于OSS.Http类库是几年前我参照RestSharp的思路,完成的一个轻量型Http请求框架。因为时间较久底层使用的还是HttpWebRequest,这次基本上是完全重构,这篇文章主要包含 1. HttpClient的介绍,2. 重构的思路, 3. 容易遇到的问题。
一. httpclient的基本介绍
HttpClient应该是在.net framework4.5版本左右引用的新功能,在此之前常用的是HttpWebRequest,相比较而言,前者更加的简单清晰,最重要的是完全支持.net standard API,这也是我选择它的重要原因。
HttpClient在结构上做了很大的调整,并且是完全异步的实现,可以说从底层上完成了异步的支持,这里先介绍对应的几个主要类:
1. HtttpRequestMessage
请求的基本信息,请求地址,请求动作等,此值是在HttpClient发起请求的方法中当参数传入,与他对应的是响应 HttpResponseMessage
2. HttpContent
请求的内容体,主要包含请求的具体内容,contenttype,contentlenght等,是HtttpRequestMessage的一个属性,这两个都包含Headers属性,但是范围分别不一样,这个是很容易混淆出错的地方,我给做了简单分类:
HttpRequestMessage的头部(HttpRequestHeaders )主要是请求的属性,如Accept,UserAgent,AcceptEncoding等http链接的基本属性。
HttpContent的头部(HttpContentHeaders)主要是当前请求内容的属性,主要有:Allow,Content-Encoding,Content-Length,Content-Type ,Expires ,Last-Modified 等,详见官方类库。
HttpContent 系统提供了几个默认实现,主要如下几个:
3. HttpMessageHandler
此类主要作用是请求内容处理动作等的定义,如是否支持重定向,是否可以使用cookie,代理Proxy等,偏向于系统的设置,可以此值通过HttpClient构造函数传入其中,系统默认的提供的子类为 HttpClientHandler。
4. HttpClient
具体的请求实现调用实现,完整实现了POST,GET,Delete等Http请求方法,所有的方法最终调用的是SendAsync方法。
上边的四个主要类,构成了HttpClient请求的主要实现,如果你只是简单的使用,那么只需要关心HttpClient即可,如下:
其实在它内部已经默认实现了HttpRequestMessage和HttpClientHandler的赋值。
虽然简单介绍,但是基本上可以看出,HttpClient的实现做了非常明确的分工,不是再像以前所有的设置都集中在webrequest中。分工的明确最直接的优势是HttpClient实现了多请求共用,参见博文:
The default HttpClient is the simplest way in which you can start sending requests. A single HttpClient can be used to send as many HTTP requests as you want concurrently so in many scenarios you can just create one HttpClient and then use that for all your requests.
也就是当你系统中要发起不同的请求时,可以共用一个HttpClient,而不用像HttpWebReqest基本每次请求都需要重新定义一个对象,以减少资源的消耗。
二. 重构OSS.Http
回到正题,重构我们的当前代码模块,如我所说,由于.Net Standard下完全不提供httpWebRequest的支持,直接导致了我做出重新实现的决定,因为以前httpWebRequest的简陋,所以我基本上做了很大的封装框架,上层完全不需要接触具体的底层实现,基本上实现了RestSharp的核心,有兴趣的同学可以参考代码 OSS.Http 下Old分支。
重构之前由于对HttpClient不是十分了解,本想延续已有框架流程,转换实现。不过随着对Client文档的查看研究,发现很多封装已经完全不需要,流程也发生了变化,所以删除很多原来框架下的东西,重新整理出最终的实现。
当然现在的HttpClient本身实现已经足够简单清晰,不过在很多情况下直接调用POST,GET等方法,会减少部分代码的重用,像在OSS.Social项目中,底层我只需要实现一个RestCommon方法,即可达到全局请求控制,调用方只需要提供Url,HttpMothed,Parameter即可。
这里我画了一个简单的流程图作为呈现:
流程基本没有太大的出入,代码在Github,文件的结构如下:
Mos文件下: Enum.cs 枚举类,FileParameter.cs 文件参数类,FormParameter Form表单参数类 ,OsHttpRequest 请求参数类,
OsRest.cs 是当前封装类的主要实现,同时为了保证HttpClient本身功能通用,OsRest继承自HttpClient,同时提供了RestSend方法,在这个方法中完成流程的实现并最终调用SendAsync方法执行请求。
RestUtil.cs 辅助类,完成了全局OsRest(HttpClient)的共用,并定义了一个默认HttpClientHandler实现,正常直接调用这个类就可以了。
流程中的执行用户自定义设置,可以在OSHttpRequest中的RequestSet委托属性中设置,例如可以设置访问类型是json:
三. 容易遇到的问题
虽然整个重构后的代码已经不多了,但是应该还是有些问题可以给大家分享下
1. Header赋值问题,请参见我第一部分,一定要分清不同Headers,否则就可能给你报不正确的值错误
2. 可以发现上边的流程图中有个“是否是Get”的判断,因为如果是Get请求,Content是不能赋值的,就像在HttpWebReqest中,如果get请求调用了GetRequestStream方法,会有“无法发送具有此谓词类型的内容正文”的异常错误。当然如果你使用的是OSS.Http作为请求,那么就没有这个问题了。
3. 和上传文件同时上传的表单参数,与单独的表单参数提交,是不一样的,请注意处理,不懂得参见OsRest类即可,已经做了处理。
如果你还有其他问题,或者对后续的更新感兴趣,请关注公众号(OSSCoder):