VBA: XMLHTTP request fails until I toggle POST/GET - excel

Okay, I need the internet's collective wisdom on this one.
I'm calling an API from Excel (code abbreviated, most of this code I got online. Need to use late binding, but not sure if MSXML2.XMLHTTP.6.0 is the best to use - Excel 365):
Dim body as String
Dim request as Object
Dim isAsync as Boolean
Set request = CreateObject("MSXML2.XMLHTTP.6.0")
isAsync = False
body = "...just some query params"
With request
.Open "POST", authUrl, isAsync
.setRequestHeader "Content-Type", "application/x-www-form-urlencoded"
.setRequestHeader "User-Agent", "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.0)"
.send body
'spin wheels whilst waiting for response
While request.ReadyState <> 4
DoEvents
Wend
Debug.print(.responseText)
End With
The issue I'm having is that the first time this code runs, it fails on the .send body line with the following error:
However, if I change "POST" to "GET" it will fail again, albeit because the server doesn't accept that method apparently. But then if I change it back to POST and run it again, it runs fine!
Then, if I save the file, close Excel, open the file again and try to run, again it's the same error as above!
So why am I having to "shake" it like this, toggling the method back and forth? It doesn't make any sense to me why that works. Obviously, I can't tell my end users to mess with the code like this, so I'd like to know what's happening. What can I test and try?
Update (May 5):
If I put a breakpoint at .send body then try to step over that (it errors), and debug, I see that request.status has a value of 12019, which is apparently some kind of bogus IE error code (?). I am running this on a machine that has IE 11, Edge, and Chrome installed. Would changing the User Agent string do anything? I tried the Chrome string as given on MDN, as well as just "MyApp" but it didn't change anything.
At this point, I'm wondering if I have to just do On Error Resume Next, try it as-is, then send the request again with GET and then again with POST and see if that works, although that's a hateful, hacky workaround.

Related

Why do I suddenly get an error when trying to send a google search using a MSXML2.XMLHTTP object?

I'm using this script I compiled to get details about the documentaries that I watch. Based on the filename, I get a documentary name, I then compile a google search string, I search google, get google's response, open the correct link from it, and get the data. This used to work until a few days ago. Today, I tried running my script, and I get an error right at the very beginning, while calling the function that does the google search. This is the function:
Public Function getHTTP(ByVal url As String) As String
With CreateObject("MSXML2.XMLHTTP")
.Open "GET", url, False
.send
getHTTP = StrConv(.responseBody, vbUnicode)
End With
End Function
"url" is compiled based on different criteria. Right now it is:
https://www.google.com/search?hl=en&source=hp&q=site:docuwiki.net+++"secrets+in+our+dna+"
The error triggers on the ".send" line. The error is:
Run-time error '-2147024891 (80070005)': Access denied.
If I then click on debug and try to run the script again (from the current point), I get another error:
Run-time error '2147467259 (80004005)': Unspecified error.
I don't think I made any changes to the system between the last time it worked and now. Haven't installed anything, no updates, nothing. I should mention that I'm on a fairly old laptop, using MSOffice 2007 under Windows 7.
Here's a minimal reproducible example. This causes the aforementioned error (that it wouldn't have a couple of weeks ago).
Public Function getHTTP(ByVal url As String) As String
With CreateObject("MSXML2.XMLHTTP")
.Open "GET", url, False
Debug.Print url
.send
getHTTP = StrConv(.responseBody, vbUnicode)
End With
End Function
Sub testing123()
Dim a, URL1
URL1 = "https://www.google.com/search?hl=en&source=hp&q=site:docuwiki.net+bbc+""beautiful+minds+"""
a = lcase(getHTTP(URL1))
End Sub
Apparently, the problem was caused by not having opened Internet Explorer in a long time. When I tested my URLs, I only did so in Chrome. When I tried to paste the same URL in IE and access it, I got one of those cookie confirmation pages. After agreeing to the cookies and closing IE, I tested the script again and it worked like it used to.

REST API Excel VBA GET call with variable input

I am trying to perform a GET call with a where clause on an id. The back end is set up and the call works when you put the URL into a browser however it does take some time to load. However, when I try the GET call from Excel I get an HTTP Error: 500. "Reason: Illegal Character in query at position: 54"
My variable portion of the URL is /?q={"user_id":"abc12345"}
The VBA input is /?q={""user_id"":""" & vUser & """}"
The 54th position is the first bracket. Does vba require some other syntax?
I also tried putting in a wait function to see if it wasn't waiting for the execution. Didn't Help.
Any idea why it works in the browser but not in the VBA?
Below is the Object I am using:
Set http = CreateObject("WinHttp.WinHttpRequest.5.1")
http.SetAutoLogonPolicy 0
http.Open "GET", URL, False
http.send

Excel VBA MSXML2.XMLHTTP60 PUT request fail on "send" action

I'm creating an integration with Jira on Excel using VBA.
I'm able to login securely (using POST) and retrieve Jira issues (using GET) using Jira's API.
Now I'm trying to update issues, and the HTTP verb on Jira's API is GET. This shouldn't be a problem, but the fact is I can't even make the request.
Here's my current code - it's based on the POST code, but I'm not sure if I'm missing some other params.
I'm omitting some variable declarations - this is not the issue!
Public JiraService As New MSXML2.XMLHTTP60
JiraDataUrl = "https://atlassian.XXXXXXXX.com/jira/rest/api/2/issue/" & JiraId
body = "{""fields"" : {""customfield_13800"":""2011-10-03""}}"
Call JiraLogin(user, pass) 'This calls another Sub that log into Jira
JiraService.Open "PUT", JiraDataUrl, False
JiraService.setRequestHeader "Content-Type", "application/json"
JiraService.setRequestHeader "Accept", "application/json"
JiraService.setRequestHeader "X-Atlassian-Token:", "nocheck"
JiraService.send body
jsonText = JiraService.responseText
sStatus = JiraService.Status
When I run the script, it's interrupted by excel with the following messages:
I click on Debug and the JiraService.send body part is marked on the code
If I click on play, I get the final error (This method cannot be called after the send method has been called), that doesn't make any sense to me:
If I replace PUT for POST it runs normally, but Jira API returns with 405 HTTP error - as the method I'm trying to use requires a PUT.
Any idea on what am I doing wrong in this code?
I am doing the same thing right now, and I've figured out, that updating an issue requers async connection:
JiraService.Open "PUT", URL, True
In that case
JiraService.send data
will be succesful, but JiraService.status returns 1223! However, an issue field will be updated...
There is a discussion with the same "problem" with MSXML2.XMLHTTP60 and it seems, 1223 code is a kind of "success code":
https://social.msdn.microsoft.com/Forums/en-US/c4911cd8-caba-4c25-b71c-fe2e1a7ef8be/update-sharepoint-list-metadata-using-rest-from-vba-using-msxml2xmlhttp60

Vb script getting timeout error

Some background
1st solution: I have built an excel solution which imports the needed data using a from text data source.
I had chosen this method as the URL which send the data will be sending a csv file. Once the data is received many process and conditions have to be applied on this.
Cause of concern: This takes a lot of time to get the data more than 6 mins. Which causes the excel to hang.
In my 2nd similar solution :- i had chosen to call a vb script from excel to do the downloading of data. This works very well for this solution.
Now, i want to implement this method for 1st solution.
My main issue: The vb script used in the 2nd solution fails with the below error.
Error:- C:\Users-user id-\Desktop\script\download_moni.vbs(15, 1) msxml6.dll: The operation timed out
My code for VB Script:-
Set args = WScript.Arguments
Url = "url given here"
dim xHttp: Set xHttp = createobject("MSXML2.ServerXMLHTTP.6.0")
dim bStrm: Set bStrm = createobject("Adodb.Stream")
xHttp.Open "GET", Url, False 'Open socket to get the website
xHttp.Send 'send request
with bStrm
.type = 1 '//binary
.open
.write xHttp.responseBody
.savetofile "C:\Ticket\Monitoring.csv", 2 '//overwrite
end with
wscript.echo("Download complete")
Please let me know how to fix the timeout error.
You say that "[it] takes a lot of time to get the data more than 6 mins." Yet the default timeout of the library you are using seems to be 30 seconds for receiving data (source).
I am not familiar with the MSXML2.ServerXMLHTTP library you are using, but Google reveals that there is a setTimeouts method, which can be used to increase various timeouts of your library.

Excel Internet Explorer Automation Causes IE Environment to Crash

I've successfully automated a VBA Excel macro to iterate through a loop and hit a series of URL's to trigger a server-side script - this is simply done with:
myIE.Navigate ("http://someURL.php?VARIABLE=" & var_string)
where var_string is assigned within the loop as it iterates through. Before this, I've cleared cache, cookies and history with:
Shell "RunDll32.exe InetCpl.cpl,ClearMyTracksByProcess #
I've tried many #'s including 8,2, 16 etc to see if any of these had an effect (and combination of #'s).
The issue I am having, is that although the entire script SOMETIMES works, if I were to run it a second time the line where I navigate to the URL fails to call the URL even though the domain/URL is fully live and functional. Any other URL I manually type into the IE window works just fine - just not the one I am calling inside the loop. IE is locking me out of that domain temporarily. If I come back to the script a few hours from last running it, it generally works.
Again the domain is functional and script is fine - I verify it all the time with another machine.
It's as if I am setting something environmentally and breaking Internet Explorer within VBA even though the script is absurdly simple.
I've tried CreateObject(), GetObject as well as InternetExplorerMedium for myIE object.
If all you need is to "touch" that URL for its side effects, you can as well use a XMLHTTP object. In VBA, go to menu Tools, then References and choose Microsoft XML, v6.0. Then:
Dim Request As New XMLHTTP
Request.open "GET", Url & "?VARIABLE=" & var_string, False
Request.send
' Check Request.status, probably for 200
Some notes:
You may want to use POST instead of GET, if you're having problems with caching
You should pass the data in the POST body, if the server can handle it
The value of var_string should be escaped, in this case, URL encoded
If you don't want to block waiting for responses, you can make requests asynchronously (True third argument to open)
Following these notes, here's a more elaborate example:
Dim Request As New XMLHTTP
Request.open "POST", Url, True
Dim Handler As New CXMLHTTPHandler
Handler.Initialize Request
Set Request.onreadystatechange = Handler
Request.setRequestHeader "Content-Type", "application/x-www-form-urlencoded"
Request.send UrlEncode("VARIABLE") & "=" & UrlEncode(var_string)
' This returns immediately, the check must now be done in the Handler
For the missing pieces, here's the code for CXMLHTTPHandler, which I actually found through stackoverflow, and a definition of UrlEncode at stackoverflow.
You should specialize CXMLHTTPHandler for your needs, probably even make it accept an AddressOf a procedure and call it in the actual default procedure. The default procedure should set the m_xmlHttp to Nothing when m_xmlHttp.readyState is 4.
EDIT 1: If your request code is in a loop, you need to break the Dim ... New statements in two, to ensure you're using fresh objects:
Dim Request As XMLHTTP
Set Request = New XMLHTTP
Request.open "POST", Url, True
Dim Handler As CXMLHTTPHandler
Set Handler = New CXMLHTTPHandler
Handler.Initialize Request
Set Request.onreadystatechange = Handler
Request.setRequestHeader "Content-Type", "application/x-www-form-urlencoded"
Request.send UrlEncode("VARIABLE") & "=" & UrlEncode(var_string)
' This returns immediately, the check must now be done in the Handler

Resources