Trouble with RESTful API POST command

Get help with using AutoHotkey (v1.1 and older) and its commands and hotkeys
contabaiswa
Posts: 4
Joined: 21 Aug 2019, 03:24

Trouble with RESTful API POST command

Post by contabaiswa » 21 Aug 2019, 04:09

Hi all,

I'm new here and trying to automate a bookkeeping program using it's (non-documented) API. Using the chrome devtools I found out that the following happens when pressing the 'create new customer' button:
  • POST /customer-form?FileID=2664ad26-4d80-4335-ad64-02deb67cd731&Referrer=0d8573d8-07be-414b-91d1-b64f2c74b36d HTTP/1.1
    Host: localhost:63345
    Connection: keep-alive
    Content-Length: 599
    Accept: */*
    X-Requested-With: XMLHttpRequest
    User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/76.0.3809.100 Safari/537.36
    Sec-Fetch-Mode: cors
    Content-Type: application/json; charset=UTF-8
    Origin: http localhost:63345 Broken Link for safety
    Sec-Fetch-Site: same-origin
    Referer: http localhost:63345 Broken Link for safety/customer-form?FileID=2664ad26-4d80-4335-ad64-02deb67cd731&Referrer=0d8573d8-07be-414b-91d1-b64f2c74b36d
    Accept-Encoding: gzip, deflate, br
    Accept-Language: nl-NL,nl;q=0.9,en-US;q=0.8,en;q=0.7
    FileID=2664ad26-4d80-4335-ad64-02deb67cd731&Referrer=0d8573d8-07be-414b-91d1-b64f2c74b36d

    {"Name":"Testnaam","BillingAddress":"Testadres 22\n5230 AK Pietdorp","Email":"contabaiswa@outlook.com","BusinessIdentifier":null,"CustomFields":{"e8e8dd97e5f94bb7a5e9390b8c56923e":"06-16546846"},"Code":null,"Inactive":false,"DeliveryAddress":null,"Obsolete_SouthAfrica_VAT_Number":null,"Obsolete_Philippines_TIN_Number":null,"Obsolete_Telephone":null,"Obsolete_Fax":null,"Obsolete_Mobile":null,"Obsolete_Notes":null,"Obsolete_HasStartingBalance":false,"Obsolete_StartingBalanceType":"Debit","Key":{"id":"00000000-0000-0000-0000-000000000000","text":"00000000000000000000000000000000"},"Timestamp":0}
To replicate this manually I wrote this code:

Code: Select all

;Setting variables
    port := 52150 ;this value changes each time the program is launched
    EndPoint := "http localhost: "  Broken Link for safety . port . "/customer-form?FileID=2664ad26-4d80-4335-ad64-02deb67cd731&Referrer=0d8573d8-07be-414b-91d1-b64f2c74b36d"
    Content = ({"Name":"Testnaam2","BillingAddress2":"Testadres 23\n5230 AK Pietdorp","Email":"myemail@outlook.com","BusinessIdentifier":null,"CustomFields":{"e8e8dd97e5f94bb7a5e9390b8c56923e":"06-16546846"},"Code":null,"Inactive":false,"DeliveryAddress":null,"Obsolete_SouthAfrica_VAT_Number":null,"Obsolete_Philippines_TIN_Number":null,"Obsolete_Telephone":null,"Obsolete_Fax":null,"Obsolete_Mobile":null,"Obsolete_Notes":null,"Obsolete_HasStartingBalance":false,"Obsolete_StartingBalanceType":"Debit","Key":{"id":"00000000-0000-0000-0000-000000000000","text":"00000000000000000000000000000000"},"Timestamp":0})  ;I put my own values in here for testing purposes

;Contact the API
Man := ComObjCreate("WinHttp.WinHttpRequest.5.1")
Man.Open("POST", EndPoint)
Man.SetRequestHeader("Host", "localhost:" . port) ;I included the requestheaders that were shown in DevTools
Man.SetRequestHeader("Connection", "keep-alive")
;~ Man.SetRequestHeader("Content-Length", "602") ;Commented this one out, doesn't seem to make a difference though
Man.SetRequestHeader("Accept", "*/*") 
Man.SetRequestHeader("X-Requested-With", "XMLHttpRequest")
Man.SetRequestHeader("User-Agent","Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/76.0.3809.100 Safari/537.36")
Man.SetRequestHeader("Sec-Fetch-Mode","cors")
Man.SetRequestHeader("Sec-Fetch-Site","same-origin")
Man.SetRequestHeader("Content-Type", "application/json")
Man.SetRequestHeader("Origin", "http localhost: "  Broken Link for safety . port)
Man.SetRequestHeader("Sec-Fetch-Site", "same-origin")
Man.SetRequestHeader("Referer", "http localhost: "  Broken Link for safety . port . "/customer-form?FileID=2664ad26-4d80-4335-ad64-02deb67cd731&Referrer=0d8573d8-07be-414b-91d1-b64f2c74b36d")
Man.SetRequestHeader("Accept-Encoding","gzip, deflate, br")
Man.SetRequestHeader("Accept-Language","nl-NL,nl;q=0.9,en-US;q=0.8,en;q=0.7")

;Send data to API
Man.Send(Content)
Result := Man.ResponseText
Status := Man.Status
msgbox % "status: " Status "`n`nresult: " Result
This however results in a status 500 code with the result:
  • <div style="overflow-x: scroll; font-family: monospace; background-color: #ffffee; border-radius: 10px; border: 1px solid red; padding: 50px; position: absolute; top: 100px; left: 100px; right: 100px; font-size: 16px"><div style="font-size: 32px; font-weight: bold">Internal Error</div><pre>19.7.50 (Desktop)</pre><pre style="color: #ccc"><b>/customer-form?FileID=2664ad26-4d80-4335-ad64-02deb67cd731&Referrer=0d8573d8-07be-414b-91d1-b64f2c74b36d</b></pre><pre style="color: #ccc"><b>Newtonsoft.Json.JsonReaderException: Unexpected character encountered while parsing value: (. Path '', line 0, position 0.
    bij Newtonsoft.Json.JsonTextReader.ParseValue()
    bij Newtonsoft.Json.JsonReader.ReadForType(JsonContract contract, Boolean hasConverter)
    bij Newtonsoft.Json.Serialization.JsonSerializerInternalReader.Deserialize(JsonReader reader, Type objectType, Boolean checkAdditionalContent)
    bij Newtonsoft.Json.JsonSerializer.DeserializeInternal(JsonReader reader, Type objectType)
    bij Newtonsoft.Json.JsonConvert.DeserializeObject(String value, Type type, JsonSerializerSettings settings)
    bij Newtonsoft.Json.JsonConvert.DeserializeObject[T](String value, JsonSerializerSettings settings)
    bij ManagerServer.HttpHandlers.Form`1.Post()
    bij ManagerServer.HttpApplication.ProcessRequest(HttpRequest request, HttpResponse response)</b></pre>
    <!--
    512 bytes of padding to suppress Internet Explorer's "Friendly error messages"

    Several frequently-seen status codes have "friendly" error messages
    that Internet Explorer 5.x displays and that effectively mask the
    actual text message that the server sends.
    However, these "friendly" error messages are only displayed if the
    response that is sent to the client is less than or equal to a
    specified threshold.
    For example, to see the exact text of an HTTP 500 response,
    the content length must be greater than 512 bytes.
    --></div>
----------------------------------------------
I have two questions about this:
1. What am I doing wrong?
2. The local host port is dynamic and changes each times the program starts. Is there a way to autodetect the port?
tmplinshi
Posts: 1604
Joined: 01 Oct 2013, 14:57

Re: Trouble with RESTful API POST command

Post by tmplinshi » 21 Aug 2019, 09:22

Remove the outside brackets from Content

Code: Select all

Content = {"Name":"Testnaam2"..................}

Code: Select all

var1 = (abc)

var2 =
(
abc
)

MsgBox % var1 ; --> (abc)
MsgBox % var2 ; --> abc
contabaiswa
Posts: 4
Joined: 21 Aug 2019, 03:24

Re: Trouble with RESTful API POST command

Post by contabaiswa » 21 Aug 2019, 10:29

That did it, thank you very much!

Now all that remains is my 2nd question: The local host port is dynamic and changes each times the program starts. Is there a way to autodetect the port?
tmplinshi
Posts: 1604
Joined: 01 Oct 2013, 14:57

Re: Trouble with RESTful API POST command

Post by tmplinshi » 21 Aug 2019, 12:48

Try:

Code: Select all

port := GetOpenedPort("program.exe") ; Change "program.exe" to the app name you're using
MsgBox % port

GetOpenedPort(ExeName) {
	s := RunWaitOne("netstat -bna -p tcp")
	if RegExMatch(s, "`as):(\d+)[^\r\n]+\s+\[\Q" ExeName "\E\]", match)
		return match1
}

; https://www.autohotkey.com/docs/commands/Run.htm#Examples
RunWaitOne(command) {
	; WshShell object: http://msdn.microsoft.com/en-us/library/aew9yb99¬
	shell := ComObjCreate("WScript.Shell")
	; Execute a single command via cmd.exe
	exec := shell.Exec(ComSpec " /C " command)
	; Read and return the command's output
	return exec.StdOut.ReadAll()
}
ahkrpa
Posts: 17
Joined: 16 Apr 2019, 17:34

Re: Trouble with RESTful API POST command

Post by ahkrpa » 21 Aug 2019, 13:35

@tmplinshi , this is wonderful. Thank you for sharing.
contabaiswa
Posts: 4
Joined: 21 Aug 2019, 03:24

Re: Trouble with RESTful API POST command

Post by contabaiswa » 22 Aug 2019, 06:05

Yes tmplinshi, great stuff!

However the msgbox gives an empty result, when manually running the netstat command in cmd I get the message "You don't have the rights to ...". Running cmd as admin solves this (adding the /K option in the script for comspec solves that part I guess).

Running cmd as admin with the netstat command, a few exe-names seem to be missing. Instead it says "Can not obtain ownership information".

Any ideas?
tmplinshi
Posts: 1604
Joined: 01 Oct 2013, 14:57

Re: Trouble with RESTful API POST command

Post by tmplinshi » 22 Aug 2019, 16:39

Thanks to just me's GetTcpTable()

Code: Select all

MsgBox % GetListenPort("program.exe")

GetListenPort(ExeName) {
	Process, Exist, %ExeName%
	If !(TargetPID := ErrorLevel)
		Throw, "Process " ExeName " Not Exist."

	Size := TCP := 0
	DllCall("Iphlpapi\GetExtendedTcpTable", "Ptr", &TCP, "UIntP", Size, "UInt", 0, "UInt", 2, "UInt", 5, "UInt", 0, "UInt")
	VarSetCapacity(TCP, Size, 0)
	If !DllCall("Iphlpapi\GetExtendedTcpTable", "Ptr", &TCP, "UIntP", Size, "UInt", 1, "Uint", 2, "UInt", 5, "UInt", 0, "UInt") {
		Entries := NumGet(TCP, "UInt")
		Addr := &TCP + 4 ; skip dwNumEntries
		Loop, %Entries% {
			State := NumGet(Addr + 0, "UInt")
			PID   := NumGet(Addr + 20, "UInt")
			If (PID = TargetPID && State = 2) { ; LISTEN=2
				LocalPort := NumGet(Addr + 8, "UShort")
				Return ((LocalPort&0xff00)>>8)|((LocalPort&0xff)<<8)
			}
			Addr += 24 ; size of MIB_TCPROW_OWNER_PID structure
		}
	}
}
If there're multiple instances of the exe running..
Spoiler
contabaiswa
Posts: 4
Joined: 21 Aug 2019, 03:24

Re: Trouble with RESTful API POST command

Post by contabaiswa » 08 Oct 2019, 03:32

Sorry for my late response, I've been away for a while,

Thanks but that also gives another blank result
Post Reply

Return to “Ask for Help (v1)”