Page 1 of 1

Help Form-data API

Posted: 04 Dec 2021, 12:44
by thalesduarte
Hello, good morning.
I'd like to assist anyone if you have the proper knowledge.

I'm trying to send an XML file via api, however, it needs it to be sent via post form-data request

I tried to look at several tutorials / libraries in the AHK forum, however, without success of sending.

If I submit this request using Postman, I can do it without any problems, but when Using AHK, I'm not able to succeed.

Here are examples of my attempts.
In case anyone can help me, I'll be grateful.

The idea of the requisition is this:
Image
in the file[] in the value field, the file that will be sent by the script is defined.

Follows prototype for evaluation

Code: Select all

oWhr := ComObjCreate("WinHttp.WinHttpRequest.5.1")
oWhr.Open("POST", "https://sitedoapi.com/requisicao", false)
arquivo:= "C:\2.xml"
boxe := {"boxe/File": false}

objParam := { "file[]": arquivo, "query": boxe}
CreateFormData(postData, hdr_ContentType, objParam)

oWhr.Send(postData)
oWhr.WaitForResponse()

msgbox, % oWhr.ResponseText
The request he returns to me is this:
{"error":{"code":500,"errors":[],"message":"HTTP 415 Unsupported Media Type"}}

If anyone knows, could you give me a light?

Best regards
Thales

Re: Help Form-data API

Posted: 06 Dec 2021, 10:18
by thalesduarte
Page up!

Re: Help Form-data API

Posted: 06 Dec 2021, 10:53
by tank
we cant guess what you fuctio does, but your arent setting any headers, we dont know if you xml matches the schema, is well formed , etc.

Re: Help Form-data API

Posted: 06 Dec 2021, 11:34
by thalesduarte
@tank
Thanks for reply

I forgot to say everything about this script, i'm trying to send this XML via api

Code: Select all

<xml>value test</xml>
I'm using createformdata.ahk for this function viewtopic.php?f=6&t=7647


I'm just in trouble sending this request by metaform/formdata

I dont know how i can make this work :(

Re: Help Form-data API

Posted: 06 Dec 2021, 11:49
by tank
well that isnt well formed as no type declaration.
https://www.w3resource.com/xml/well-formed.php

and you arent sending any headers.

example
owhr.setRequestHeader("Content-type", "application/x-www-form-urlencoded")

it is ecxpplained inthe example

Code: Select all

whr.SetRequestHeader("Content-Type", hdr_ContentType)
whr.SetRequestHeader("User-Agent", "Mozilla/5.0 (Windows NT 6.1; WOW64; Trident/7.0; rv:11.0) like Gecko") ; probably doesnt matter but better safe than sorry

Re: Help Form-data API

Posted: 06 Dec 2021, 12:07
by thalesduarte
Hello! @tank
Thanks for the help, but i'm begginer with json language.

I'm trying to send XML via API.

Irf i did it manualy, using the software Postman, i can do it without problem, but when i try to use AHK for this, i cant :\
This form-data dont work with me.


Do you know how i can make that json script for AHK?


I exported this part of Postman, which is what's missing from my script.

Code: Select all

				"body": {
					"mode": "formdata",
					"formdata": [
						{
							"key": "file[]",
							"contentType": "application/xml",
							"type": "file",
							"src": [
								"/C:/DOCUMENT.xml"
							]
						},
						{
							"key": "query",
							"value": "{\"boxe/File\": false}",
							"contentType": "application/json",
							"type": "text"
						}
					]
				}
Do you know how i can convert this part for ahk language?

Re: Help Form-data API

Posted: 06 Dec 2021, 12:38
by Chunjee
Does that unnamed api accept json formatted requests? That is more common than XML I thought.

Re: Help Form-data API

Posted: 06 Dec 2021, 12:54
by thalesduarte
@Chunjee

I dont know if accept, i guess yes, i'm so newbie at this, XD.

I'm trying to get some functions at the forum for make this work
This is my full code

Code: Select all

; Begin the Code
URL := "https://api.onvio.com.br/dominio/invoice/v3/batches"
TokenValidator := "XYZ"
BearerValidator := "BEARER"

SendXMLAPI := ComObjCreate("WinHttp.WinHttpRequest.5.1")
SendXMLAPI.Open("POST", URL , False)
SendXMLAPI.SetRequestHeader("x-integration-key", TokenValidator)
SendXMLAPI.SetRequestHeader("Authorization","Bearer "  BearerValidator)
SendXMLAPI.SetRequestHeader("User-Agent", "Mozilla/5.0 (Windows NT 6.1; WOW64; Trident/7.0; rv:11.0) like Gecko") 
File := "C:\Users\Thales\Desktop\TESTES\1.XML"
boxe := {"boxe/File": false}
objParam := {"file[]": {json: "{'" file "':'application/xml'}"}, "query": {json: "{" boxe ":'application/json'}"}}

CreateFormData(postData, ContentType, objParam)

SendXMLAPI.Send(postData)


Result:= SendXMLAPI.ResponseText
MSGBOX, % Result
My problem is the postData

The result:

Code: Select all

{"error":{"code":500,"errors":[],"message":"HTTP 415 Unsupported Media Type"}}
Look the function of CreateFormData.ahk

Code: Select all

; ===============================================================================

; CreateFormData
; 2020-06-08 - Added a temporary approach to handling the json content-type
;	           example: objParam := { "key": {json: "{'k':'v'}"} }
;
/*
	CreateFormData - Creates "multipart/form-data" for http post
	https://www.autohotkey.com/boards/viewtopic.php?t=7647
	Usage: CreateFormData(ByRef retData, ByRef retHeader, objParam)
		retData   - (out) Data used for HTTP POST.
		retHeader - (out) Content-Type header used for HTTP POST.
		objParam  - (in)  An object defines the form parameters.
		            To specify files, use array as the value. Example:
		                objParam := { "key1": "value1"
		                            , "upload[]": ["1.png", "2.png"] }
	Requirements: BinArr.ahk -- https://gist.github.com/tmplinshi/a97d9a99b9aa5a65fd20
	Version    :      / 2020-06-08 - Added a temporary approach to handling the json content-type
	                                 example: objParam := { "key": {json: "{'k':'v'}"} }
	             1.30 / 2019-01-13 - The file parameters are now placed at the end of the retData
	             1.20 / 2016-06-17 - Added CreateFormData_WinInet(), which can be used for VxE's HTTPRequest().
	             1.10 / 2015-06-23 - Fixed a bug
	             1.00 / 2015-05-14
*/

; Used for WinHttp.WinHttpRequest.5.1, Msxml2.XMLHTTP ...
CreateFormData(ByRef retData, ByRef retHeader, objParam) {
	New CreateFormData(retData, retHeader, objParam)
}

; Used for WinInet
CreateFormData_WinInet(ByRef retData, ByRef retHeader, objParam) {
	New CreateFormData(safeArr, retHeader, objParam)

	size := safeArr.MaxIndex() + 1
	VarSetCapacity(retData, size, 1)
	DllCall("oleaut32\SafeArrayAccessData", "ptr", ComObjValue(safeArr), "ptr*", pdata)
	DllCall("RtlMoveMemory", "ptr", &retData, "ptr", pdata, "ptr", size)
	DllCall("oleaut32\SafeArrayUnaccessData", "ptr", ComObjValue(safeArr))
}

Class CreateFormData {

	__New(ByRef retData, ByRef retHeader, objParam) {

		CRLF := "`r`n"

		Boundary := this.RandomBoundary()
		BoundaryLine := "------------------------------" . Boundary

		; Loop input paramters
		binArrs := []
		fileArrs := []
		For k, v in objParam
		{
			If IsObject(v) {
				if v.HasKey("json")
				{
					str := BoundaryLine . CRLF
					     . "Content-Type: application/json" . CRLF . CRLF
					     . v.json . CRLF
					binArrs.Push( BinArr_FromString(str) )
				}
				else
				{
					For i, FileName in v
					{
						str := BoundaryLine . CRLF
						     . "Content-Disposition: form-data; name=""" . k . """; filename=""" . FileName . """" . CRLF
						     . "Content-Type: " . this.MimeType(FileName) . CRLF . CRLF
						fileArrs.Push( BinArr_FromString(str) )
						fileArrs.Push( BinArr_FromFile(FileName) )
						fileArrs.Push( BinArr_FromString(CRLF) )
					}
				}
				
			} Else {
				str := BoundaryLine . CRLF
				     . "Content-Disposition: form-data; name=""" . k """" . CRLF . CRLF
				     . v . CRLF
				binArrs.Push( BinArr_FromString(str) )
			}
		}

		binArrs.push( fileArrs* )

		str := BoundaryLine . "--" . CRLF
		binArrs.Push( BinArr_FromString(str) )

		retData := BinArr_Join(binArrs*)
		retHeader := "multipart/form-data; boundary=----------------------------" . Boundary
	}

	RandomBoundary() {
		str := "0|1|2|3|4|5|6|7|8|9|a|b|c|d|e|f|g|h|i|j|k|l|m|n|o|p|q|r|s|t|u|v|w|x|y|z"
		Sort, str, D| Random
		str := StrReplace(str, "|")
		Return SubStr(str, 1, 12)
	}

	MimeType(FileName) {
		n := FileOpen(FileName, "r").ReadUInt()
		Return (n        = 0x474E5089) ? "image/png"
		     : (n        = 0x38464947) ? "image/gif"
		     : (n&0xFFFF = 0x4D42    ) ? "image/bmp"
		     : (n&0xFFFF = 0xD8FF    ) ? "image/jpeg"
		     : (n&0xFFFF = 0x4949    ) ? "image/tiff"
		     : (n&0xFFFF = 0x4D4D    ) ? "image/tiff"
		     : "application/octet-stream"
	}

}

; ===============================================================================
BinArr_FromString(str) {
	oADO := ComObjCreate("ADODB.Stream")

	oADO.Type := 2 ; adTypeText
	oADO.Mode := 3 ; adModeReadWrite
	oADO.Open
	oADO.Charset := "UTF-8"
	oADO.WriteText(str)

	oADO.Position := 0
	oADO.Type := 1 ; adTypeBinary
	oADO.Position := 3 ; Skip UTF-8 BOM
	return oADO.Read, oADO.Close
}

BinArr_FromFile(FileName) {
	oADO := ComObjCreate("ADODB.Stream")

	oADO.Type := 1 ; adTypeBinary
	oADO.Open
	oADO.LoadFromFile(FileName)
	return oADO.Read, oADO.Close
}

BinArr_Join(Arrays*) {
	oADO := ComObjCreate("ADODB.Stream")

	oADO.Type := 1 ; adTypeBinary
	oADO.Mode := 3 ; adModeReadWrite
	oADO.Open
	For i, arr in Arrays
		oADO.Write(arr)
	oADO.Position := 0
	return oADO.Read, oADO.Close
}

BinArr_ToString(BinArr, Encoding := "UTF-8") {
	oADO := ComObjCreate("ADODB.Stream")

	oADO.Type := 1 ; adTypeBinary
	oADO.Mode := 3 ; adModeReadWrite
	oADO.Open
	oADO.Write(BinArr)

	oADO.Position := 0
	oADO.Type := 2 ; adTypeText
	oADO.Charset  := Encoding 
	return oADO.ReadText, oADO.Close
}

BinArr_ToFile(BinArr, FileName) {
	oADO := ComObjCreate("ADODB.Stream")

	oADO.Type := 1 ; adTypeBinary
	oADO.Open
	oADO.Write(BinArr)
	oADO.SaveToFile(FileName, 2)
	oADO.Close
}

; ==================================================================================
; by Coco, https://www.autohotkey.com/boards/viewtopic.php?t=627
Jxon_Load(ByRef src, args*)
{
	static q := Chr(34)

	key := "", is_key := false
	stack := [ tree := [] ]
	is_arr := { (tree): 1 }
	next := q . "{[01234567890-tfn"
	pos := 0
	while ( (ch := SubStr(src, ++pos, 1)) != "" )
	{
		if InStr(" `t`n`r", ch)
			continue
		if !InStr(next, ch, true)
		{
			ln := ObjLength(StrSplit(SubStr(src, 1, pos), "`n"))
			col := pos - InStr(src, "`n",, -(StrLen(src)-pos+1))

			msg := Format("{}: line {} col {} (char {})"
			,   (next == "")      ? ["Extra data", ch := SubStr(src, pos)][1]
			  : (next == "'")     ? "Unterminated string starting at"
			  : (next == "\")     ? "Invalid \escape"
			  : (next == ":")     ? "Expecting ':' delimiter"
			  : (next == q)       ? "Expecting object key enclosed in double quotes"
			  : (next == q . "}") ? "Expecting object key enclosed in double quotes or object closing '}'"
			  : (next == ",}")    ? "Expecting ',' delimiter or object closing '}'"
			  : (next == ",]")    ? "Expecting ',' delimiter or array closing ']'"
			  : [ "Expecting JSON value(string, number, [true, false, null], object or array)"
			    , ch := SubStr(src, pos, (SubStr(src, pos)~="[\]\},\s]|$")-1) ][1]
			, ln, col, pos)

			throw Exception(msg, -1, ch)
		}

		is_array := is_arr[obj := stack[1]]

		if i := InStr("{[", ch)
		{
			val := (proto := args[i]) ? new proto : {}
			is_array? ObjPush(obj, val) : obj[key] := val
			ObjInsertAt(stack, 1, val)
			
			is_arr[val] := !(is_key := ch == "{")
			next := q . (is_key ? "}" : "{[]0123456789-tfn")
		}

		else if InStr("}]", ch)
		{
			ObjRemoveAt(stack, 1)
			next := stack[1]==tree ? "" : is_arr[stack[1]] ? ",]" : ",}"
		}

		else if InStr(",:", ch)
		{
			is_key := (!is_array && ch == ",")
			next := is_key ? q : q . "{[0123456789-tfn"
		}

		else ; string | number | true | false | null
		{
			if (ch == q) ; string
			{
				i := pos
				while i := InStr(src, q,, i+1)
				{
					val := StrReplace(SubStr(src, pos+1, i-pos-1), "\\", "\u005C")
					static end := A_AhkVersion<"2" ? 0 : -1
					if (SubStr(val, end) != "\")
						break
				}
				if !i ? (pos--, next := "'") : 0
					continue

				pos := i ; update pos

				  val := StrReplace(val,    "\/",  "/")
				, val := StrReplace(val, "\" . q,    q)
				, val := StrReplace(val,    "\b", "`b")
				, val := StrReplace(val,    "\f", "`f")
				, val := StrReplace(val,    "\n", "`n")
				, val := StrReplace(val,    "\r", "`r")
				, val := StrReplace(val,    "\t", "`t")

				i := 0
				while i := InStr(val, "\",, i+1)
				{
					if (SubStr(val, i+1, 1) != "u") ? (pos -= StrLen(SubStr(val, i)), next := "\") : 0
						continue 2

					; \uXXXX - JSON unicode escape sequence
					xxxx := Abs("0x" . SubStr(val, i+2, 4))
					if (A_IsUnicode || xxxx < 0x100)
						val := SubStr(val, 1, i-1) . Chr(xxxx) . SubStr(val, i+6)
				}

				if is_key
				{
					key := val, next := ":"
					continue
				}
			}

			else ; number | true | false | null
			{
				val := SubStr(src, pos, i := RegExMatch(src, "[\]\},\s]|$",, pos)-pos)
			
			; For numerical values, numerify integers and keep floats as is.
			; I'm not yet sure if I should numerify floats in v2.0-a ...
				static number := "number", integer := "integer"
				if val is %number%
				{
					if val is %integer%
						val += 0
				}
			; in v1.1, true,false,A_PtrSize,A_IsUnicode,A_Index,A_EventInfo,
			; SOMETIMES return strings due to certain optimizations. Since it
			; is just 'SOMETIMES', numerify to be consistent w/ v2.0-a
				else if (val == "true" || val == "false")
					val := %val% + 0
			; AHK_H has built-in null, can't do 'val := %value%' where value == "null"
			; as it would raise an exception in AHK_H(overriding built-in var)
				else if (val == "null")
					val := ""
			; any other values are invalid, continue to trigger error
				else if (pos--, next := "#")
					continue
				
				pos += i-1
			}
			
			is_array? ObjPush(obj, val) : obj[key] := val
			next := obj==tree ? "" : is_array ? ",]" : ",}"
		}
	}

	return tree[1]
}

Jxon_Dump(obj, indent:="", lvl:=1)
{
	static q := Chr(34)

	if IsObject(obj)
	{
		static Type := Func("Type")
		if Type ? (Type.Call(obj) != "Object") : (ObjGetCapacity(obj) == "")
			throw Exception("Object type not supported.", -1, Format("<Object at 0x{:p}>", &obj))

		is_array := 0
		for k in obj
			is_array := k == A_Index
		until !is_array

		static integer := "integer"
		if indent is %integer%
		{
			if (indent < 0)
				throw Exception("Indent parameter must be a postive integer.", -1, indent)
			spaces := indent, indent := ""
			Loop % spaces
				indent .= " "
		}
		indt := ""
		Loop, % indent ? lvl : 0
			indt .= indent

		lvl += 1, out := "" ; Make #Warn happy
		for k, v in obj
		{
			if IsObject(k) || (k == "")
				throw Exception("Invalid object key.", -1, k ? Format("<Object at 0x{:p}>", &obj) : "<blank>")
			
			if !is_array
				out .= ( ObjGetCapacity([k], 1) ? Jxon_Dump(k) : q . k . q ) ;// key
				    .  ( indent ? ": " : ":" ) ; token + padding
			out .= Jxon_Dump(v, indent, lvl) ; value
			    .  ( indent ? ",`n" . indt : "," ) ; token + indent
		}

		if (out != "")
		{
			out := Trim(out, ",`n" . indent)
			if (indent != "")
				out := "`n" . indt . out . "`n" . SubStr(indt, StrLen(indent)+1)
		}
		
		return is_array ? "[" . out . "]" : "{" . out . "}"
	}

	; Number
	else if (ObjGetCapacity([obj], 1) == "")
		return obj

	; String (null -> not supported by AHK)
	if (obj != "")
	{
		  obj := StrReplace(obj,  "\",    "\\")
		, obj := StrReplace(obj,  "/",    "\/")
		, obj := StrReplace(obj,    q, "\" . q)
		, obj := StrReplace(obj, "`b",    "\b")
		, obj := StrReplace(obj, "`f",    "\f")
		, obj := StrReplace(obj, "`n",    "\n")
		, obj := StrReplace(obj, "`r",    "\r")
		, obj := StrReplace(obj, "`t",    "\t")

		static needle := (A_AhkVersion<"2" ? "O)" : "") . "[^\x20-\x7e]"
		while RegExMatch(obj, needle, m)
			obj := StrReplace(obj, m[0], Format("\u{:04X}", Ord(m[0])))
	}
	
	return q . obj . q
}

Re: Help Form-data API

Posted: 06 Dec 2021, 13:25
by tank
in general xml and json are not interchangable. you are going to have to send your data in the format expected.

Re: Help Form-data API

Posted: 06 Dec 2021, 13:30
by tank
without seeing raw request i may not be able to guess what does and doesnt work

the request itself is a json so your content type headee needs to match that.

Re: Help Form-data API

Posted: 06 Dec 2021, 14:27
by thalesduarte
@tank

Do you know if AHK has any kind of COMOBJECT for POST API FORM DATA with XML files?

Thanks!

Re: Help Form-data API

Posted: 06 Dec 2021, 15:05
by tank
you are very confused. this has nothing to do with ahk and everything to do with what the server will accept. you seem unwilling/unable to accept that you cant just send whatever you want and expect it to work

Re: Help Form-data API

Posted: 06 Dec 2021, 19:12
by Chunjee
Uhh best I can tell that API does accept json requests; it responds with json btw. But it also requires OAuth to authenticate which I don't think is very easy with ahk.

I could be wrong on all of that.