前言

最近在写WebClientApi这个组件,底层使用HttpClient,发现HttpClient有许多低级的错误,使用者一不小心就可能会正常的去调用它的这些错误,得不到预期的结果。本文我把我认为是问题或缺陷的地方指出(但不一定是问题或缺陷,可能是个人理解错误),后人也许可以跳过这些缺陷。

缺陷1

请求头Cookie与HttpClientHandler的CookieContainer水火不容

默认的,HttpClient会使用默认的HttpClientHandler,默认的HttpClientHandler的UseCookies是true,也就是说,默认情况下HttpClient就有间接的CookieContainer可以使用。但UseCookies为true了,请求头的Cookie就不会提交,请求头的Cookie就不会提交,请求头的Cookie就不会提交。所以注意了,如果把Cookie提交给服务器的话,当UseCookies为true时,只有把cookie值一一写入CookieContainer,提交的cookie才生效;否则只有写入请求头,提交的cookie才生效。

缺陷2

HttpClientHandler.SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)有问题

HttpClient.DefaultRequestHeaders,当请求头有设置("Connection", "keep-alive"),进行第一次请求的时候,参数request没问题,但执行SendAsync逻辑体之后,服务端收到的Connection不标准,收到请求头为Connection: keep-alive,Keep-Alive ,如果服务器兼容性不好,处理请求之后就会断开连接。奇葩的是,第二次以后都不会出现重复的keep-alive,如果设置为("Connection", ""),第一次ok,后面的都没有Connection请求头了,全部报断开...

由于HttpClient不是bcl,所以没找到源代码,反编译看了一下,想真正的重写这个SendAsync难度大,干脆就来个将错就错,错错得对的法子,绕开这个问题

/// <summary>
/// 修复keep-alive问题的HttpClientHandler
/// </summary>
class KeepAliveHandler : HttpClientHandler
{
/// <summary>
/// 发送次数
/// </summary>
private int sendTimes = ; /// <summary>
/// 是否keepAlive
/// </summary>
private readonly bool keepAlive; /// <summary>
/// keep-alive的HttpClientHandler
/// </summary>
/// <param name="keepAlive">keepAlive</param>
public KeepAliveHandler(bool keepAlive)
{
this.keepAlive = keepAlive;
} /// <summary>
/// 发送请求
/// </summary>
/// <param name="request"></param>
/// <param name="cancellationToken"></param>
/// <returns></returns>
protected override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, System.Threading.CancellationToken cancellationToken)
{
request.Headers.Remove("Connection");
if (this.keepAlive == true)
{
if (Interlocked.CompareExchange(ref this.sendTimes, , ) == )
{
request.Headers.Add("Connection", string.Empty);
}
else
{
request.Headers.Add("Connection", "keep-alive");
}
}
return base.SendAsync(request, cancellationToken);
}
}

缺陷3

MultipartContent的boundary问题

随便new 它一个实例,可以看到它的 Conent-Type与大众客户端不一样,符合不符合标准我不清楚,大概是这样Content-Type: multipart/form-data; boundary="boundary"

注意它的两个双引号了,我用PostMan没有引号,Postman如下图:

如果你想得到没用引号的boundary,可以这样修改:

var boundary = Guid.NewGuid().ToString();
var parameter = new NameValueHeaderValue("boundary", boundary);
httpContent = new MultipartContent("form-data", boundary); httpContent.Headers.ContentType.Parameters.Clear();
httpContent.Headers.ContentType.Parameters.Add(parameter);

缺陷4:

MultipartFormDataContent的Add(HttpContent content,  string xxx ...)的问题

这两个方法生成的表单,boundary问题继承了它老爸,自己生成内容时也有问题,PostMan是生成name="{name}"; filename="{filename}",每个都有又引号;但MultipartFormDataContent生成的是name={name}; filename={filename},双引号不见了,但它的IIS貌似能兼容,其它的就不知道了。

如果你想得到双引号的内容,MultipartFormDataContent这个类可以废了,用它的老爸MultipartContent吧。

要添加文件项,可以使用下面这个类,直接Add到MultipartContent对象:

/// <summary>
/// 表示文件内容
/// </summary>
class MulitpartFileContent : StreamContent
{
/// <summary>
/// 文件内容
/// </summary>
/// <param name="stream">文件流</param>
/// <param name="name">名称</param>
/// <param name="fileName">文件名</param>
/// <param name="contentType">文件Mime</param>
public MulitpartFileContent(Stream stream, string name, string fileName, string contentType)
: base(stream)
{
if (this.Headers.ContentDisposition == null)
{
var disposition = new ContentDispositionHeaderValue("form-data");
disposition.Name = string.Format("\"{0}\"", name);
disposition.FileName = string.Format("\"{0}\"", fileName);
this.Headers.ContentDisposition = disposition;
} if (string.IsNullOrEmpty(contentType))
{
contentType = "application/octet-stream";
}
this.Headers.ContentType = new MediaTypeHeaderValue(contentType);
}
}

class MulitpartFileContent

要添加文本项,可以使用下面这个类,直接Add到MultipartContent对象:

/// <summary>
/// 表示文本内容
/// </summary>
class MulitpartTextContent : StringContent
{
/// <summary>
/// 文本内容
/// </summary>
/// <param name="name">名称</param>
/// <param name="value">文本</param>
public MulitpartTextContent(string name, string value)
: base(value == null ? string.Empty : value)
{
if (this.Headers.ContentDisposition == null)
{
var disposition = new ContentDispositionHeaderValue("form-data");
disposition.Name = string.Format("\"{0}\"", name);
this.Headers.ContentDisposition = disposition;
}
this.Headers.Remove("Content-Type");
}
}

MulitpartTextContent

当前状态

正在火力开WebApiClient中,关于HttpClient更多缺陷与绕过方法,正在发现的路上,欢迎使用我的WebApiClient

.Net45下HttpClient的几个缺陷的更多相关文章

  1. Cocos开发中Visual Studio下HttpClient开发环境设置

    Cocos2d-x 3.x将与网络通信相关的类集成到libNetwork类库工程中,这其中包括了HttpClient类.我们需要在Visual Studio解决方案中添加libNetwork类库工程. ...

  2. 9102年了,汇总下HttpClient问题,封印一个

    如果找的是core的HttpClientFactory 出门右转. 官方写法,高并发下,TCP连接不能快速释放,导致端口占完,无法连接 Dispose 不是马上关闭tcp连接 主动关闭的一方为什么不能 ...

  3. 记录下httpclient 发送请求 服务端用@RequestBody 自动接收参数 报415

    注解是post方式,那么检查以下内容:1. 你是否用了post请求2. 请求是否发送了数据3. 请求内容格式需要是 application/json .jquery 设置 contentType,-- ...

  4. .net的retrofit--WebApiClient底层篇

    前言 本篇文章的内容是WebApiClient底层说明,也是WebApiClient系列接近尾声的一篇文章,如果你没有阅读过之前的的相关文章,可能会觉得本文章的内容断层,WebApiClient系列文 ...

  5. 使用WebApiClient请求和管理Restful Api

    前言 本篇文章的内容是WebApiClient应用说明篇,如果你没有了解过WebApiClient,可以先阅读以下相关文章: WebApi client 的面向切面编程 我来给.Net设计一款Http ...

  6. Cocos发育Visual Studio下一个HttpClient开发环境设置

    Cocos2d-x 3.x相关类集成到网络通信libNetwork图书馆project于.这其中包括:HttpClient分类. 我们需要在Visual Studio溶液中加入libNetwork图书 ...

  7. 程序员节应该写博客之.NET下使用HTTP请求的正确姿势

    程序员节应该写博客之.NET下使用HTTP请求的正确姿势 一.前言 去年9月份的时候我看到过外国朋友关于.NET Framework下HttpClient缺陷的分析后对HttpClient有了一定的了 ...

  8. .NET下使用HTTP请求的正确姿势

    来源:Lewis.Zou cnblogs.com/modestmt/p/7724821.html 一.前言 去年9月份的时候我看到过外国朋友关于.NET Framework下HttpClient缺陷的 ...

  9. HttpClient Coder Example

    Example 1:   HttpClient httpClient = new HttpClient();                 httpClient.getHostConfigurati ...

随机推荐

  1. How to quickly become effective when joining a new company

    How to quickly become effective when joining a new company The other day my colleague Richard asked ...

  2. Swagger

    Swagger是一个规范和完整的框架,用于生成.描述.调用和可视化 RESTful 风格的 Web 服务 http://swagger.io/

  3. 【PHP】Sublime下PHP网站开发指南

    Sublime下PHP网站开发指南 作者:白宁超 2017年3月16日11:03:17 摘要:随着单位开发项目的需求,关于政务办公多年来一直使用php开发管理平台.笔者早年asp开发经验算是有些帮助, ...

  4. DOM-----style属性对照表

    CSS和Java script 标签对照表 盒子标签和属性对照 CSS语法 (不区分大小写) Java script 语法 (区分大小写) border border border-bottom bo ...

  5. Markdown 简单指北

    Markdown is intended to be as easy-to-read and easy-to-write as is feasible. Markdown 简介 Markdown是一种 ...

  6. PYQT4 : QSystemTrayIcon练习

    照着demo自己做了一遍,练练手 import sys from PyQt4 import QtGui from PyQt4 import QtCore class SysTray(QtGui.QDi ...

  7. 修改Tomcat服务器默认端口

  8. JavaScript操作DOM节点

    DOM (文档对象模型(Document Object Model)) 文档对象模型(Document Object Model,简称DOM),是W3C组织推荐的处理可扩展标志语言的标准编程接口.在网 ...

  9. 通俗易懂的Java序列化原理

    序列化能干吗? (1)通过序列化可以把数据永久地保存到硬盘上(通常存放在文件里) (2)利用序列化实现远程通信,即在网络上传送对象的字节序列. 网上搜索的都是这样的解释,抽象,难懂. 我当时看见这两问 ...

  10. C# 爬虫 Jumony html解析

    前言 前几天写了个爬虫,然后认识到了自己的不足.感谢 "倚天照海- -" ,我通过你推荐的文章,意外的发现了html解析的类库——Jumony. 研究了2天,我发现这个东西简单粗暴 ...