Search code examples
excelvbawinhttpfastapiwinhttprequest

How to send a form-data POST request with VBA in Excel using WinHTTPRequest


I am building an API using FastAPI that must be accessible from Excel VBA. FastAPI's OAuth2 authentication mechanism requires me to send a "form-data" POST request, but I don't know how to do this using WinHTTPRequest in VBA.

I already have a function which get plain JSON from the API, but this is a GET function and I'm not sure where to specify the "form-data" part in the body, nor where to put the username and password key-value pairs.

Here is a simple VBA GET that handles some errors. How would I modify this to do a form-data POST with username and password fields?

Public Function getreq(url As String)
    Dim req As WinHttpRequest
    Dim JsonString As String
    Dim jp As Object
    Dim resp As String
    Dim errorstring As String

    Set req = New WinHttpRequest
    ' req.SetRequestHeaderxxx ?
    ' this is where auth will go via POST form-data username and password?
    req.Open "GET", url
    On Error GoTo errhand:
        req.Send
        resp = req.ResponseText
        If resp = "Internal Server Error" Then
            resp = "{'error': 'Internal server error'}"
        End If
        getreq = resp
    Exit Function

errhand:

    Select Case Err.Number
        Case -2147012894 'Code for Timeout
            getreq = "{'error': 'Request timeout'}"
        Case -2147012891 'Code for Invalid URL
            getreq = "{'error': 'Bad url'}"
        Case -2147012867 'Code for Invalid URL
            getreq = "{'error': 'Cannot establish connection'}"
        Case Else 'Add more Errorcodes here if wanted
            errorstring = "Errornumber: " & Err.Number & vbNewLine & "Errordescription: " & Error(Err.Number)
            getreq = "{'error': '" & errorstring & "'}"
    End Select
End Function

Solution

  • Instead of opening a GET request req.Open "GET", url you need to open a POST

    req.Open "POST", url, False
    

    Then you can use setRequestHeader to set which content type you are going to send and what you accept in return.

    req.setRequestHeader "Content-Type", "multipart/form-data"
    req.setRequestHeader "Accept", "application/xml"
    

    For authorization you would also need to create a request header. This depends on what your API uses.

    req.setRequestHeader "Authorization:", "Bearer " & EncodeBase64("Your-access-token")
    

    Finally you send your request

    req.send (YourData)
    

    Have a look into this answer which shows how the data structure for multipart/form-data is generated.


    If a WinHTTPRequest does not work I recommend to try the following

    Set objHttp = CreateObject("MSXML2.ServerXMLHTTP.6.0")
    URL = "https:///commercial/payments-processing/v1/test/token"
    objHttp.Open "POST", URL, False
    objHttp.setRequestHeader "Content-Type", "multipart/form-data"
    objHttp.setRequestHeader "Accept", "application/xml"
    objHttp.setRequestHeader "Authorization:", "Bearer [Access Token]"
    objHttp.send (strPaylodValue)
    strResponseStatus = objHttp.Status
    strResponseText = objHttp.ResponseText
    strResponseText = CStr(strResponseText)