11.7
Why do I get an array error doing this?
| UploadValues(Uri, String, NameValueCollection) |
Uploads the specified name/value collection to the resource identified by the specified URI, using the specified Method. |
The UploadValues method of the web client returns a Byte array.
def var foo as "System.Byte[]".
foo = <your statement>.
Now you need to do something with the byte array. You could either pass it to another .net method if appropriate or convert it to something usable in the Progress world. The method below will convert a byte array to a memptr. From there you can do what's needed.
METHOD PUBLIC STATIC MEMPTR ByteArrayToMemptr( nBytes AS "System.Byte[]" ):
DEFINE VARIABLE nPtr AS System.IntPtr NO-UNDO.
DEFINE VARIABLE mPtr AS MEMPTR NO-UNDO.
DEFINE VARIABLE PointerLoc AS INT64 NO-UNDO.
set-size(mPtr) = nBytes:LENGTH.
/* In 11.7, due to the new compile used, Progress is much more likely to choose
a memory pointer that exceeds the 32 bit limit of an integer.
Using this intermediate INT64 solves the problem */
PointerLoc = GET-POINTER-VALUE(mPtr).
nPtr = NEW System.IntPtr(PointerLoc).
System.Runtime.InteropServices.Marshal:Copy(nBytes, 0, nPtr, nBytes:LENGTH).
RETURN mPtr.
FINALLY:
nPtr = ?.
nBytes = ?.
set-size(mPtr) = 0.
END.
END METHOD.
Array on the right-hand side is the Byte[] array returned by UploadValues. You're assigning it to what appears to be a longchar. That does not compute...
aaah, what would be the correct approach ? Can't do a
def var lcResponse as longchar extent no-undo.
The UploadValues method of the web client returns a Byte array.
def var foo as "System.Byte[]".
foo = <your statement>.
Now you need to do something with the byte array. You could either pass it to another .net method if appropriate or convert it to something usable in the Progress world. The method below will convert a byte array to a memptr. From there you can do what's needed.
METHOD PUBLIC STATIC MEMPTR ByteArrayToMemptr( nBytes AS "System.Byte[]" ):
DEFINE VARIABLE nPtr AS System.IntPtr NO-UNDO.
DEFINE VARIABLE mPtr AS MEMPTR NO-UNDO.
DEFINE VARIABLE PointerLoc AS INT64 NO-UNDO.
set-size(mPtr) = nBytes:LENGTH.
/* In 11.7, due to the new compile used, Progress is much more likely to choose
a memory pointer that exceeds the 32 bit limit of an integer.
Using this intermediate INT64 solves the problem */
PointerLoc = GET-POINTER-VALUE(mPtr).
nPtr = NEW System.IntPtr(PointerLoc).
System.Runtime.InteropServices.Marshal:Copy(nBytes, 0, nPtr, nBytes:LENGTH).
RETURN mPtr.
FINALLY:
nPtr = ?.
nBytes = ?.
set-size(mPtr) = 0.
END.
END METHOD.
The correct approach would be to read the manuals so that you actually know what you are doing ;-)
An even better approach would be to stop using .NET and use the 4GL instead, although with SSL/TLS that can be tricky I have to admit.
Byte[] suggests memptr...
I am taking the Learning by trying, but I should probably do learning by Reading [:)].
I forgot to check the Return value… It went from string to byte array… my bad ! [:)]
I was uploading and was not thinging that the returnvalue was changed.
Sorry for that !
I've created an ABL equivalent using the HTTP client. It's at https://github.com/PeterJudge-PSC/http_samples/blob/master/http_client/post_form_name_value.p
The meat of it is below (minus variable defs and usings)
assign hc = ClientBuilder:Build():Client
// the first param is a realm you can leave blank
creds = new Credentials('':u, clientId, clientSecret)
.
// build the request
req = RequestBuilder:Post(tokenEndpoint,
// prior to 11.7.3 you had to pass in a valid object here. in 11.7.3+ you can not
new StringStringMap() )
:AddFormData('client_id', 'oidc_ovf_conf')
:AddFormData('grant_type','authorization_code')
:UsingBasicAuthentication(creds)
:Request.
// make the request
resp = hc:Execute(req).
// process the response
message
resp:StatusCode skip // 200 is all went well
resp:ContentType skip // something like application/json or application/x-www-form-urlencoded
resp:ContentLength skip // number of bytes, if you care
view-as alert-box.
// this Entity property is DEFINED as Progress.Lang.Object
// but the actual class/type will be something else; typically something that matches the ContentType
// now get the response data in a nice, strongly-typed Object form
// We can either decide what to do based on the ContentType or on the object's type
// approach 1
case resp:ContentType:
when 'application/json':u then
assign jsonData = cast(resp:Entity, JsonObject).
// other types
end case.