尽管有效负载与成功的浏览器发起的请求相同,但通过 HTTPRequest 与 VBA 的“无效的多部分有效负载格式”

Posted

技术标签:

【中文标题】尽管有效负载与成功的浏览器发起的请求相同,但通过 HTTPRequest 与 VBA 的“无效的多部分有效负载格式”【英文标题】:"Invalid multipart payload format" via HTTPRequest with VBA despite identical payload to successful browser initiated requests 【发布时间】:2022-01-21 11:45:16 【问题描述】:

我正在尝试使用 VBA 将文件上传到 Web 服务,遵循通过 Web 浏览器上传文档时发送的表单数据多部分结构,但是我收到“400:无效的多部分有效负载格式”响应,尽管发送(据我所知)请求中有相同的有效负载。

对于我的测试用例,我创建了一个名为 test.txt 的 txt 文档,其中包含“TestContents”。我已经使用网络浏览器上传了这个文件,并且也尝试过使用 VBA(如下所示)。花一些时间将来自 Chrome 的有效负载与我自己的 VBA 生成的有效负载进行比较,我看不出有任何差异。

我正在使用以下代码创建多部分有效负载并将其发送到 Web 服务(URL 部分混淆),并使用以下问题帮助重写了几次以解决此问题:[ https://***.com/questions/50110601/upload-a-picture-to-file-io-http-post-in-vba]

(对于内部的任何混乱,我深表歉意 - 我已经更改和重写了很长一段时间了,所以可能会有一些不整洁)

Private Function toArray(inData As String)

    Dim objADODB As Object: Set objADODB = CreateObject("ADODB.Stream")
    
     objADODB.Type = 2
     objADODB.Charset = "_autodetect"
     objADODB.Open
     objADODB.WriteText (inData)
     objADODB.Position = 0
     objADODB.Type = 1
     toArray = objADODB.Read()

     Set objADODB = Nothing
End Function

Private Function readBinary(filePath As String)

    Dim objADODB As Object: Set objADODB = CreateObject("ADODB.Stream")
    
    objADODB.Type = 1
    objADODB.Open
    objADODB.LoadFromFile filePath
    readBinary = objADODB.Read
    objADODB.Close
    
    Set objADODB = Nothing
End Function

Public Sub sendDocument(ByVal inID As String, ByVal tags As String, ByVal docContentType As String, ByVal docFilePath As String)

     Dim objADODB As Object
     Dim objHTTP As Object
    
     Dim boundary As String: boundary = "----WebKitFormBoundaryeeYChAY7UlBEI63d" 'Set like this so i can debug like-for-like with browser payload
     Dim docFileName As String: docFileName = Mid(docFilePath, InStrRev(docFilePath, "\") + 1)

     '(structure is file, material, tags if there are any)
     Set objADODB = CreateObject("ADODB.Stream")
    
     With objADODB
         .Type = 1
         .Open
        
         .Write toArray("--" & boundary & Chr(10))
         .Write toArray("Content-Disposition: form-data; name=""file""; filename=""" & docFileName & """" & Chr(10) & _
                 "Content-Type: " & docContentType & Chr(10) & Chr(10))
         .Write readBinary(docFilePath)
         .Write toArray(Chr(10) & "--" & boundary & Chr(10))
         .Write toArray("Content-Disposition: form-data; name=""material""" & Chr(10) & Chr(10) & inID & Chr(10))
         If tags <> "" Then .Write toArray("Content-Disposition: form-data; name=""tags""" & Chr(10) & Chr(10) & tags & Chr(10))
         .Write toArray("--" & boundary & "--")
         .Position = 0
     End With

     If Not validateID(inID) Then
         MsgBox ("ID must be 4-5 digits long")
         Exit Sub
     End If
    
     If auth = "" Then
         MsgBox "Login is required. Click OK to log in"
         Call getAuth
     End If
    
     Set objHTTP = CreateObject("MSXML2.ServerXMLHTTP")
    
     With objHTTP
         .Open "POST", "https://xxx.xxx/hapi/document", False
         .setRequestHeader "Authorization", auth
         .setRequestHeader "Content-Type", "multipart/form-data; boundary=" & boundary
         .send (objADODB.Read())
         MsgBox .responseText
     End With
    
     bStatusOK = objHTTP.status = 200
    
     If objHTTP.status = 401 Then
         MsgBox ("Login is invalid/expired. Please reauthenticate")
         Call getAuth
     End If
End Sub

当查看请求的表单数据时,上面会产生一个与 Chrome 的检查器窗口中显示的相同的有效负载,即:

------WebKitFormBoundaryeeYChAY7UlBEI63d
Content-Disposition: form-data; name="file"; filename="Test.txt"
Content-Type: text/plain

TestContents
------WebKitFormBoundaryeeYChAY7UlBEI63d
Content-Disposition: form-data; name="material"

16145
------WebKitFormBoundaryeeYChAY7UlBEI63d--

我开始怀疑它的编码不正确。我注意到浏览器调用使用了标头

Accept-Encoding: gzip, deflate, br

..当我尝试复制此类型或任何单一类型(br 除外)时,我仍然收到相同的消息,但以 VBA 本地窗口无法理解的格式回复。我已经成功处理了其他发送 JSON 或文本有效负载的 POST 和 PUT 请求,而无需指定 Accept-Encoding,因此我不确定这是否是继续前进的正确路径。

如果有人能够在这方面提供一些帮助,我将非常感激。

谢谢!

【问题讨论】:

尝试使用vbCrLF 代替Chr(10) 啊,这是让我感到最简单的事情,这很有魅力,非常感谢!! 【参考方案1】:

感谢 CDP1802 的评论解决了!需要VbCrLf 而不是Chr(10)

构建表单数据的工作部分现在如下所示:

Set objADODB = CreateObject("ADODB.Stream")
    
With objADODB
    .Type = 1
    .Open
    
    .Write toArray("--" & boundary & vbCrLf)
    .Write toArray("Content-Disposition: form-data; name=""file""; filename=""" & docFileName & """" & vbCrLf & _
             "Content-Type: " & docContentType & vbCrLf & vbCrLf)
    .Write readBinary(docFilePath)
    .Write toArray(vbCrLf & "--" & boundary & vbCrLf)
    .Write toArray("Content-Disposition: form-data; name=""material""" & vbCrLf & vbCrLf & inID & vbCrLf)
    If tags <> "" Then
        .Write toArray("--" & boundary & vbCrLf)
        .Write toArray("Content-Disposition: form-data; name=""tags""" & vbCrLf & vbCrLf & tags & vbCrLf)
    End If
    .Write toArray("--" & boundary & "--")
    .Position = 0
End With

【讨论】:

【参考方案2】:

如果您有标签参数,它将缺少开始边界。

If tags <> "" Then
    .Write toArray("--" & boundary & vbCrLf)
    .Write toArray("Content-Disposition: form-data; name=""tags""" & vbCrLf & vbCrLf & tags & vbCrLf)
End If

【讨论】:

很好,谢谢!我在重写时错过了这一点。

以上是关于尽管有效负载与成功的浏览器发起的请求相同,但通过 HTTPRequest 与 VBA 的“无效的多部分有效负载格式”的主要内容,如果未能解决你的问题,请参考以下文章

如何在scrapy中发送带有标头和有效负载的Post请求

密码在请求有效负载 devtools 中可见

Web常见攻击手段-CSRF攻击

跨域问题解决方案

PDF 表单提交 - 请求有效负载 PHP 解析器

2 相同的 HTTP 请求给出不同的结果?