欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

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

程序员文章站 2023-10-29 09:57:58
问题 在开发微信支付的小微商户进件接口时,需要通过表单来上传身份证图片等数据。在微信支付接口文档也说明了,需要使用 multipart/form data 的方式发送请求。.NET 提供了 类型,帮助我们构建表单请求,故有以下代码: 按照微信支付官方文档提交之后,一直提示参数错误,百思不得其解。 原 ......

问题

在开发微信支付的小微商户进件接口时,需要通过表单来上传身份证图片等数据。在微信支付接口文档也说明了,需要使用 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"}
};