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.
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 = "https://<<myDomain>>.talentlms.com/api/v1/"
strRequest = apiURL & "deleteuser/"
boundary = "----------------------------" & Format(Now, "ddmmyyyyhhmmss")
postData = "--" & boundary & vbCrLf & _
"Content-Disposition: form-data; name=""user_id""" & vbCrLf & _
"Content-Type: text/plain; charset=UTF-8" & vbCrLf & vbCrLf & _
intUserid & vbCrLf & _
"--" & boundary & vbCrLf & _
"Content-Disposition: form-data; name=""permanent""" & vbCrLf & _
"Content-Type: text/plain; charset=UTF-8" & vbCrLf & vbCrLf & _
strPermanent & vbCrLf & _
"--" & boundary & "--"
contentLen = Len(postData)
With request
.Open "POST", (strRequest), False
.setRequestHeader "Authorization", "Basic <<MyAPIKey>>=="
.setRequestHeader "Host", "<<myDomain>>.talentlms.com"
.setRequestHeader "Content-Type", "multipart/form-data; boundary=" & boundary
.setRequestHeader "content-Length", contentLen
.send (postData)
While request.ReadyState <> 4
DoEvents
Wend
strResponse = .responseText
End With
Debug.Print "Server responded with status " & request.statusText & " - code: "; 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("user_id", intUserid, "permanent", 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 = "----------------------------" & Format(Now, "ddmmyyyyhhmmss")
Dim i As Long
boundaries = ""
For i = LBound(params) To UBound(params)
boundaries = boundaries & "--" & boundary & vbCrLf & _
"Content-Disposition: form-data; name=""" & params(i) & """" & vbCrLf & _
"Content-Type: text/plain; charset=UTF-8" & vbCrLf & vbCrLf
i = i + 1
boundaries = boundaries & params(i) & vbCrLf
Next i
boundaries = boundaries & "--" & boundary & "--"
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 = "https://<<myDomain>>.talentlms.com/api/v1/"
strRequest = apiURL & "deleteuser/"
boundary = Left(postData, 44)
contentLen = Len(postData)
With request
.Open "POST", (strRequest), False
.setRequestHeader "Authorization", "Basic <<MyAPIKey>>=="
.setRequestHeader "Host", "<<myDomain>>.talentlms.com"
.setRequestHeader "Content-Type", "multipart/form-data; boundary=" & boundary
.setRequestHeader "content-Length", contentLen
.send (postData)
While request.ReadyState <> 4
DoEvents
Wend
strResponse = .responseText
End With
Debug.Print "Server responded with status " & request.statusText & " - code: "; request.status
Debug.Print postData
talentAPICall_4 = strResponse
End Function
Here are the results of both methods used:
Working:
call talentAPICall_3(3314, "yes")
Server responded with status OK - code: 200
Posted Data:
------------------------------15022023131313
Content-Disposition: form-data; name="user_id"
Content-Type: text/plain; charset=UTF-8
3314
------------------------------15022023131313
Content-Disposition: form-data; name="permanent"
Content-Type: text/plain; charset=UTF-8
yes
------------------------------15022023131313--
Not Working:
call delUser(3314, "yes")
Server responded with status Bad Request - code: 400
Posted Data:
------------------------------15022023131246
Content-Disposition: form-data; name="user_id"
Content-Type: text/plain; charset=UTF-8
3314
------------------------------15022023131246
Content-Disposition: form-data; name="permanent"
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?
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.