VBA TalentLMS API Post Request with multipart/form-data throwing error when passing Request Body with boundaries in as a string parameter

huangapple go评论67阅读模式
英文:

VBA TalentLMS API Post Request with multipart/form-data throwing error when passing Request Body with boundaries in as a string parameter

问题

我正在创建一个在MS Access中调用TalentLMS API不同端点的模块。我正在创建函数以减少每个端点所需的代码量。到目前为止,所有我的GET请求都正常工作。我也成功地创建了一个用于添加用户帐户的POST请求。我遇到的问题是,如果我在API调用函数中生成multipart/form-data(请求正文),则可以正常工作,但如果我将multipart/form-data(请求正文)作为参数传递给API调用函数,则无法正常工作。

以下是已经正常工作的代码:

' 代码...

以下是不正常工作的代码:

' 代码...

当请求正文生成并作为字符串返回时,它会作为参数传递给下一个函数。

' 代码...

在两种情况下,postData都是一个字符串,它们具有相同的参数。根据TalentLMS API,400错误代码的解释是“缺少必需参数或提供了无效类型(例如字符串)而不是整数”。在这两种情况下,postData都是一个字符串,它们具有相同的参数。

是否有人看到我漏掉了什么?

英文:

I am creating a Module in MS Access to make API calls to different endpoints in the TalentLMS API. I am creating functions to minimize the code needed for each endpoint. So far all of my GET requests are working. I have a POST request to add a user account working as well. The problem that I am running into is that I have a POST request to delete a user account that works if I generate the multipart/form-data (Request Body) in the API Call function but does not work if I pass the mutlipart/form-data (Request Body) in as a parameter to the API Call function.

This is Working I generate the request body with boundaries within the API call function as a string.

<!-- language: vb -->
Function talentAPICall_3(ByVal intUserid As Integer, ByVal strPermanent As String) As String
Dim request As New MSXML2.XMLHTTP30
Dim apiURL, boundary, postData, strRequest, strResponse As String
Dim contentLen As Long

    apiURL = &quot;https://&lt;&lt;myDomain&gt;&gt;.talentlms.com/api/v1/&quot;
    strRequest = apiURL &amp; &quot;deleteuser/&quot;
    boundary = &quot;----------------------------&quot; &amp; Format(Now, &quot;ddmmyyyyhhmmss&quot;)
    postData = &quot;--&quot; &amp; boundary &amp; vbCrLf &amp; _
        &quot;Content-Disposition: form-data; name=&quot;&quot;user_id&quot;&quot;&quot; &amp; vbCrLf &amp; _
        &quot;Content-Type: text/plain; charset=UTF-8&quot; &amp; vbCrLf &amp; vbCrLf &amp; _
        intUserid &amp; vbCrLf &amp; _
        &quot;--&quot; &amp; boundary &amp; vbCrLf &amp; _
        &quot;Content-Disposition: form-data; name=&quot;&quot;permanent&quot;&quot;&quot; &amp; vbCrLf &amp; _
        &quot;Content-Type: text/plain; charset=UTF-8&quot; &amp; vbCrLf &amp; vbCrLf &amp; _
        strPermanent &amp; vbCrLf &amp; _
        &quot;--&quot; &amp; boundary &amp; &quot;--&quot;
    contentLen = Len(postData)

    With request
            .Open &quot;POST&quot;, (strRequest), False
            .setRequestHeader &quot;Authorization&quot;, &quot;Basic &lt;&lt;MyAPIKey&gt;&gt;==&quot;
            .setRequestHeader &quot;Host&quot;, &quot;&lt;&lt;myDomain&gt;&gt;.talentlms.com&quot;
            .setRequestHeader &quot;Content-Type&quot;, &quot;multipart/form-data; boundary=&quot; &amp; boundary
            .setRequestHeader &quot;content-Length&quot;, contentLen
            .send (postData)

            While request.ReadyState &lt;&gt; 4
                DoEvents
            Wend
            strResponse = .responseText         
    End With

    Debug.Print &quot;Server responded with status &quot; &amp; request.statusText &amp; &quot; - code: &quot;; request.status
    Debug.Print postData
    talentAPICall_3 = strResponse
End Function

This is NOT Working Where I use a getBoundaries() function to pass the body request with boundaries as a string to the API call function.

Function DelUser(ByVal intUserid As Integer, ByVal strPermanent As String) As String

Dim postData, strResponse As String
Dim boundaries() As Variant

boundaries = Array(&quot;user_id&quot;, intUserid, &quot;permanent&quot;, strPermanent)

postData = getBoundaries(boundaries)

strResponse = talentAPICall_4(postData)

DelUser = strResponse

End Function

Which calls the following.

Function getBoundaries(params() As Variant) As String

Dim boundary, boundaries As String

boundary = &quot;----------------------------&quot; &amp; Format(Now, &quot;ddmmyyyyhhmmss&quot;)

Dim i As Long
boundaries = &quot;&quot;

For i = LBound(params) To UBound(params)
    boundaries = boundaries &amp; &quot;--&quot; &amp; boundary &amp; vbCrLf &amp; _
    &quot;Content-Disposition: form-data; name=&quot;&quot;&quot; &amp; params(i) &amp; &quot;&quot;&quot;&quot; &amp; vbCrLf &amp; _
    &quot;Content-Type: text/plain; charset=UTF-8&quot; &amp; vbCrLf &amp; vbCrLf
    i = i + 1
    boundaries = boundaries &amp; params(i) &amp; vbCrLf
Next i

boundaries = boundaries &amp; &quot;--&quot; &amp; boundary &amp; &quot;--&quot;

getBoundaries = boundaries

End Function

When the Request Body is generated with boundaries and returned as a string, it is then passed as a parameter to the next function.

Function talentAPICall_4(ByVal postData As String) As String

    Dim request As New MSXML2.XMLHTTP30
    Dim apiURL, boundary, strRequest, strResponse As String
    Dim contentLen As Long

    apiURL = &quot;https://&lt;&lt;myDomain&gt;&gt;.talentlms.com/api/v1/&quot;
    strRequest = apiURL &amp; &quot;deleteuser/&quot;
    boundary = Left(postData, 44)
    contentLen = Len(postData)

    With request
            .Open &quot;POST&quot;, (strRequest), False
            .setRequestHeader &quot;Authorization&quot;, &quot;Basic &lt;&lt;MyAPIKey&gt;&gt;==&quot;
            .setRequestHeader &quot;Host&quot;, &quot;&lt;&lt;myDomain&gt;&gt;.talentlms.com&quot;
            .setRequestHeader &quot;Content-Type&quot;, &quot;multipart/form-data; boundary=&quot; &amp; boundary
            .setRequestHeader &quot;content-Length&quot;, contentLen
            .send (postData)

            While request.ReadyState &lt;&gt; 4
                DoEvents
            Wend
            strResponse = .responseText         
    End With

    Debug.Print &quot;Server responded with status &quot; &amp; request.statusText &amp; &quot; - code: &quot;; request.status
    Debug.Print postData    

    talentAPICall_4 = strResponse

End Function

Here are the results of both methods used:

Working:

call talentAPICall_3(3314, &quot;yes&quot;)
Server responded with status OK - code:  200 

Posted Data:
------------------------------15022023131313
Content-Disposition: form-data; name=&quot;user_id&quot;
Content-Type: text/plain; charset=UTF-8

3314
------------------------------15022023131313
Content-Disposition: form-data; name=&quot;permanent&quot;
Content-Type: text/plain; charset=UTF-8

yes
------------------------------15022023131313--

Not Working:

call delUser(3314, &quot;yes&quot;)
Server responded with status Bad Request - code:  400 

Posted Data:
------------------------------15022023131246
Content-Disposition: form-data; name=&quot;user_id&quot;
Content-Type: text/plain; charset=UTF-8

3314
------------------------------15022023131246
Content-Disposition: form-data; name=&quot;permanent&quot;
Content-Type: text/plain; charset=UTF-8

yes
------------------------------15022023131246--

As you can see, except for the variation of the time stamp used to create the boundary, the postData from Debug.Print for both functions is the same. The TalentLMS API states that the following for the 400 error code "A required parameter is missing or an invalid type (e.g. a string) was supplied instead of an integer." In both cases postData is a String and they have the same parameters.

Anyone see what I am missing?

答案1

得分: 0

在你的“工作”代码中,边界头的长度为42,而在不工作的代码中,长度为44?

你需要移除开头的“--”...

boundary = Mid(postData, 3, 42)

可能更安全将边界传递给 getBoundaries 而不是尝试从输出中提取它。

英文:

In your "working" code the boundary header length is 42, and in the non-working code it's 44?

You need to remove the leading "--"...

boundary = Mid(postData, 3, 42)

Might be safer to pass the boundary in to getBoundaries rather than try to extract it from the output.

huangapple
  • 本文由 发表于 2023年2月16日 07:23:51
  • 转载请务必保留本文链接:https://go.coder-hub.com/75466356.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定