使用 HttpClient 进行表单提交时,遇到的问题

简介: 使用 HttpClient 进行表单提交时,遇到的问题

问题

在开发微信支付的小微商户进件接口时,需要通过表单来上传身份证图片等数据。在微信支付接口文档也说明了,需要使用 multipart/form-data 的方式发送请求。.NET 提供了 MultipartFormDataContent 类型,帮助我们构建表单请求,故有以下代码:

var form = new MultipartFormDataContent()
{
    {new StringContent("Value"),"Name},
    {new ByteArrayContent(new byte[]{}/*模拟文件数据*/),"File,"FileName}
}

按照微信支付官方文档提交之后,一直提示参数错误,百思不得其解。

原因

通过 Postman 模拟表单提交,捕获数据包,将其与 C# 的提交代码进行对比,发现了两处问题。

Postman 的原始提交:

POST http://api.mch.weixin.qq.com/secapi/mch/uploadmedia HTTP/1.1
User-Agent: PostmanRuntime/7.21.0
Accept: */*
Cache-Control: no-cache
Postman-Token: b6800c0f-3f16-4981-b661-e6d16fc1bb1e
Host: api.mch.weixin.qq.com
Content-Type: multipart/form-data; boundary=--------------------------639275760242036520206377
Accept-Encoding: gzip, deflate
Content-Length: 566
Connection: keep-alive
 
----------------------------639275760242036520206377
Content-Disposition: form-data; name="mch_id"
 
1565111111
----------------------------639275760242036520206377
Content-Disposition: form-data; name="media_hash"
 
7215E92A8F3F3D0256484EFFF53A25F6
----------------------------639275760242036520206377
Content-Disposition: form-data; name="sign_type"
 
HMAC-SHA256
----------------------------639275760242036520206377
Content-Disposition: form-data; name="sign"
 
A1D8B094FA24BE5531D1AC198DE25550
----------------------------639275760242036520206377--

C# 代码的提交:

POST http://api.mch.weixin.qq.com/secapi/mch/uploadmedia HTTP/1.1
Host: api.mch.weixin.qq.com
Content-Type: multipart/form-data; boundary="e9d5712f-7923-4ec5-8bf3-c8d5d3cd3217"
Content-Length: 502
 
--e9d5712f-7923-4ec5-8bf3-c8d5d3cd3217
Content-Type: text/plain; charset=utf-8
Content-Disposition: form-data; name=mch_id
 
 
--e9d5712f-7923-4ec5-8bf3-c8d5d3cd3217
Content-Type: text/plain; charset=utf-8
Content-Disposition: form-data; name=media_hash
 
33F15BC2D17D6FFBC18FA566EF65722E
--e9d5712f-7923-4ec5-8bf3-c8d5d3cd3217
Content-Type: text/plain; charset=utf-8
Content-Disposition: form-data; name=sign
 
1E377684F9BD583D2ED26FB367916C0C
--e9d5712f-7923-4ec5-8bf3-c8d5d3cd3217--

1. Boundary 的双引号

使用 MultipartFormDataContent 提交的表单请求,外部 Content-Typeboundary 值带有 " 号。而 Postman 提交的表单请求,它的 boudary 值是没有双引号的。

那为什么会造成这样的差异呢?参考 这篇 博客的讲解,是由于各个系统/语言针对 RFC 2046 的实现不一致导致的。针对于 MultipartFormDataContent 的行为,如果 Boundary 后面的值带有双引号是符合标准的。

RFC 2612 原文:

2) Although RFC 2046 [40] permits the boundary string to be

quoted, some existing implementations handle a quoted boundary

string incorrectly.

Boundary 的作用,是一个随机生成的字符串,在 HTTP 协议当中用于分割内部多个 Content。为什么是随机生成的呢?就是防止这个分割符跟你内部的 Content 产生重复造成意外。(C# 默认使用的是 Guid 作为随机串,你也可以在构造 MultipartFormDataContent 的时候,通过其构造函数手动指定)

2. 表单内键值对,值的双引号

第二个问题则是表单内的内容,他们的 name 键值对,其值又没有双引号,所以你得在添加 Content 的时候,得手动指定双引号。

解决

两个问题都是由于双引号导致的,所以只需要在真正发起调用之前将内部的双引号替换为空,或者将缺失的双引号添加上即可。

针对问题一,其内部的 ContentType.Parameters ,通过 LINQ 找到 boundary 的键值对,替换内部的双引号即可。

var boundaryValue = form.Headers.ContentType.Parameters.Single(p => p.Name == "boundary");
boundaryValue.Value = boundaryValue.Value.Replace("\"", String.Empty);

针对问题二,在构造内部 Content 的时候,其 Name 手动赋予双引号。

var form = new MultipartFormDataContent
{
    {new StringContent(mchId), "\"mch_id\""},
    {new ByteArrayContent(bytes), "media", $"\"{HttpUtility.UrlEncode(Path.GetFileName(imagePath))}\""},
    {new StringContent(mediaHash), "\"media_hash\""},
    {new StringContent(sign), "sign"}
};


相关文章
|
7月前
|
Java API Apache
HttpClient来完成POST请求
【6月更文挑战第4天】HttpClient来完成POST请求
38 2
|
8月前
|
前端开发 JavaScript 数据处理
ajax Fileupload多文件上传实现
【5月更文挑战第3天】使用jQuery和AjaxFileUpload.js,实现多文件上传的示例。HTML部分包括一个文件输入框和上传按钮。JavaScript部分在按钮点击时获取文件,创建FormData对象并遍历文件添加进去。然后通过Ajax以POST方式发送至'upload.php',设置禁用jQuery的数据处理和contentType自动设置。成功或失败后有相应回调处理服务器响应或错误。
78 5
|
前端开发 JavaScript
ajax Fileupload多文件上传实现案例
ajax Fileupload多文件上传实现案例
153 1
|
JSON 数据格式
OkHttp3发起POST或GET请求
OkHttp3发起POST或GET请求
504 0
|
JSON 数据格式
Okhttp post请求数据和使用中的一些问题
Okhttp post请求数据和使用中的一些问题
|
Web App开发 .NET Windows
WebApi 异步请求(HttpClient)
还是那几句话: 学无止境,精益求精 十年河东,十年河西,莫欺少年穷 学历代表你的过去,能力代表你的现在,学习代表你的将来 废话不多说,直接进入正题: 今天公司总部要求各个分公司把短信接口对接上,所谓的短信接口其实就是GET或者Post请求,接到这个任务感觉好Easy。
1056 0
|
JSON 数据格式 Java
|
JSON 数据格式
httpclient get/post请求
public static String httpPost(String url, JSONObject json) { String respContent = null; try{ HttpPost httpPost = new HttpPost(url); CloseableHttpClient client = HttpClients.
812 0