Using a x-www-form-urlencoded Rest API with Business central (to get token)

Brax007
Member Posts: 26
Hi everyone,
I'm trying to develop a function with AL that permits to Post a url encoded content.

It works fine with postman. The following procedure that I've started developping doesn't work :
Can someone help me please?
I get this response everytime :
{
"error": "invalid_request",
"error_description": "Missing form parameter: grant_type"
}
Ps : it doesn't work even without encoding the parameters.
Thanks in advance
I'm trying to develop a function with AL that permits to Post a url encoded content.

It works fine with postman. The following procedure that I've started developping doesn't work :
procedure GetToken() ResponseText: text; Var client: HttpClient; cont: HttpContent; header: HttpHeaders; response: HttpResponseMessage; Jobject: JsonObject; tmpString: Text; TypeHelper: Codeunit "Type Helper"; granttype: text; clienid: text; username: text; password: text; Begin granttype := 'xxxxx'; clienid := 'xxxxxx'; username := 'xxxxxxxxx'; password := 'xxxxxxxxxxxxx'; Jobject.Add('grant_type', TypeHelper.UrlEncode(granttype)); Jobject.Add('client_id', TypeHelper.UrlEncode(clienid)); Jobject.Add('username', TypeHelper.UrlEncode(username)); Jobject.Add('password', TypeHelper.UrlEncode(password)); Jobject.WriteTo(tmpString); cont.WriteFrom(tmpString); cont.ReadAs(tmpString); cont.GetHeaders(header); header.Add('charset', 'UTF-8'); header.Remove('Content-Type'); header.Add('Content-Type', 'application/x-www-form-urlencoded'); client.Post('xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx', cont, response); response.Content.ReadAs(ResponseText); end;
Can someone help me please?
I get this response everytime :
{
"error": "invalid_request",
"error_description": "Missing form parameter: grant_type"
}
Ps : it doesn't work even without encoding the parameters.
Thanks in advance
0
Best Answer
-
Hello @Brax007,
With your code you are sending a JSON format and you need to send something like this:grant_type=grant_type_value&client_id=client_id_value&username=username_value&password=password_value
And every value must be urlEncode like you already do.
Regards.2
Answers
-
Hello @Brax007,
With your code you are sending a JSON format and you need to send something like this:grant_type=grant_type_value&client_id=client_id_value&username=username_value&password=password_value
And every value must be urlEncode like you already do.
Regards.2 -
What could also help is to inspect the full request (body + header) from BC and the full request from Postman with a webdebugging tool.
And in addition to the reply of @ftornero your BC code is similar to a 'raw' body while you should use 'x-www-form-urlencoded'.
Code I used in C/AL:
- Put the key/value pairs in an Outstream
- TempBlob.Blob.CREATEINSTREAM(InStream);
- TempBlob.Blob.CREATEOUTSTREAM(OutStr);
- Copy to Instream
- SetContentType to application/x-www-form-urlencoded (HttpWebRequestMgt.SetContentType('application/x-www-form-urlencoded');)
- Add the stream to the body of your message: HttpWebRequestMgt.AddBodyBlob(TempBlob);0 -
Hi all,
how do we add this headers?
for GET
0 -
Hello @julkifli33, the same way as the code above in this post:
Change the "application/x-www-form-urlencoded" for "application/json" and add the two others one.
Regards.0 -
Hi @ftornero
this is my codegcontent.GetHeaders(lheaders); lheaders.Remove('Content-Type'); lheaders.Add('Content-Type', 'application/Json'); lheaders.Add('X-IBM-Client-Id', ClientID); lheaders.Add('X-IBM-Client-Secret', ClientSecret); if not gHttpClient.get(RequestUrl, gResponseMsg) then Error('API Authorization token request failed...'); gResponseMsg.Content().ReadAs(responseText); Message('%1', responseText);
but this is my return value
it supposed to be like this
in postman i tested is ok{ "returnCode": "10", "data": { "url": "https://*********" }, "info": { "fieldInfoList": [] } }
0 -
Hello @julkifli33 ,
With this code
You don't pass the RequestMsg that you need to pass.
Use
This is the URL for the documentation
https://docs.microsoft.com/en-us/dynamics365/business-central/dev-itpro/developer/methods-auto/httpclient/httpclient-data-type
Regards0 -
Hi @ftornero ,
I tried this before// Retrieve the contentHeaders associated with the content content.GetHeaders(contentHeaders); contentHeaders.Clear(); contentHeaders.Add('X-IBM-Client-Id', ClientID); contentHeaders.Add('X-IBM-Client-Secret',ClientSecret); contentHeaders.Add('Content-Type', 'application/json'); contentHeaders.Add('Accept', 'application/json'); request.Content := content; request.SetRequestUri(uri); request.Method := 'GET'; client.Send(request, response); // Read the response content as json. response.Content().ReadAs(responseText); Message('%1', responseText);
got this error message
0 -
Hello @julkifli33, the headers in AL are a little complicated, this is the way I do in an scenario like your:
var baseUrl: Text; Client: HttpClient; Content: HttpContent; RequestMsg: HttpRequestMessage; RequestHeader: HttpHeaders; ContentHeader: HttpHeaders; ResponseMSg: HttpResponseMessage; authorizationValue: Text; begin baseUrl := GetBaseURL(); Content.Clear(); // Write content if StrLen(contentText) > 0 then Content.WriteFrom(contentText); if method <> 'GET' then begin ContentHeader.Clear(); Content.GetHeaders(ContentHeader); ContentHeader.Remove('Content-Type'); if contentType <> '' then ContentHeader.Add('Content-Type', contentType) else ContentHeader.Add('Content-Type', 'application/json'); RequestMsg.Content(Content); end; RequestHeader.Clear(); RequestMsg.GetHeaders(RequestHeader); authorizationValue := CreateAuthorization(apiKey); RequestHeader.Add('Authorization', authorizationValue); RequestMsg.SetRequestUri(baseUrl + apiUrl); RequestMsg.Method(method); if Client.Send(RequestMsg, ResponseMSg) then begin ......
There is two header, so put the "Content-Type" in the first one and the autorization part in the second one, but if the method is GET I think that the "Content-Type" part is no necesary because you don't send any data in this case.
Regards1 -
0
-
0
-
Hi @ftornero ..
i am still stuck with this.
any idea or link you can share how to use azure function, and then BC call this azure function?0 -
Hello @julkifi33
Try this codepageextension 50100 CustomerListExt extends "Customer List" { actions { addafter("&Customer") { action(Singapur) { ApplicationArea = All; Caption = 'Singapur'; Promoted = true; PromotedIsBig = true; PromotedOnly = true; Image = SendTo; trigger OnAction() var CodError: Text; ResTxt: Text; begin CallGETRESTApi(CodError, ResTxt); Message(ResTxt); end; } } } procedure CallGETRESTApi(var CodigoError: Text; var ResTxt: Text): Boolean var baseUrl: Text; Client: HttpClient; RequestMsg: HttpRequestMessage; ResponseMSg: HttpResponseMessage; begin baseUrl := 'https://ftomps64.azurewebsites.net/api/Singapur?code=GFb/v6xv9auJlEx7OOrqGhnuHYBhyoOmZV0SeGU2wezbtVsTBSQNAQ=='; RequestMsg.SetRequestUri(baseUrl); RequestMsg.Method('GET'); if Client.Send(RequestMsg, ResponseMSg) then begin if ResponseMSg.IsSuccessStatusCode() then begin ResponseMSg.Content.ReadAs(ResTxt); CodigoError := ''; exit(true); end else begin CodigoError := Format(ResponseMSg.HttpStatusCode()) + ' ' + ResponseMSg.ReasonPhrase(); ResponseMSg.Content.ReadAs(ResTxt); if ResTxt = '' then ResTxt := CodigoError; exit(false); end; end else begin CodigoError := 'ERROR'; if ResponseMSg.Content.ReadAs(ResTxt) then; if ResTxt = '' then ResTxt := CodigoError; exit(false); end; end; }
And you must get this message:
This is the Azure Function Code, the Client-ID and the Client-Secret must been the correct ones.using namespace System.Net # Input bindings are passed in via param block. param($Request, $TriggerMetadata) #-------------------------------- function Execute-GetRequest #-------------------------------- ( [String] $URL ) { $WebRequest = [System.Net.WebRequest]::Create($URL) $WebRequest.Headers.Add("X-IBM-Client-ID", "xxxxx-xxxxx-xxxx-xxxxx-xxxxxx") $WebRequest.Headers.Add("X-IBM-Client-Secret", "SssssSSSSSsssSSSSssss") $WebRequest.ContentType = "application/json" $WebRequest.Method = "GET" try { $resp = $WebRequest.GetResponse() $responseStream = $resp.GetResponseStream() $Reader = [System.IO.StreamReader]($responseStream) $ReturnXml = $Reader.ReadToEnd() $responseStream.Close() return $ReturnXml } catch { Write-Error $Error[0] } } $url = "https://apisandbox.iras.gov.sg/iras/sb/Authentication/CorpPassAuth?callback_url=https://businesscentral.dynamics.com/sandbox&tax_agent=false&state=StanleyState&scope=GSTF7SubCP" $ret = Execute-GetRequest $url # Associate values to output bindings by calling 'Push-OutputBinding'. Push-OutputBinding -Name Response -Value ([HttpResponseContext]@{ StatusCode = [HttpStatusCode]::OK Body = $ret })
You could elaborate the Azure function to get as parameters the Method, the URL, the X-IBM-Client-ID, etc.
This Azure function will be active for a couple of days.
Regards.0 -
Hi @ftornero ,
thanks for the effort
I am not really familiar with this azure function.
are we able to pass parameter to this azure function?
such as clientid, clientsecret and url0 -
Hello @julkifli33,
You must to create the Azure function, and sure you can pass parameters to it.
The AL code to do the new callpageextension 50100 CustomerListExt extends "Customer List" { actions { addafter("&Customer") { action(Singapur) { ApplicationArea = All; Caption = 'Singapur'; Promoted = true; PromotedIsBig = true; PromotedOnly = true; Image = SendTo; trigger OnAction() var CodError: Text; ResTxt: Text; begin CallGETRESTApi(CodError, ResTxt); Message(ResTxt); end; } } } procedure CallGETRESTApi(var CodigoError: Text; var ResTxt: Text): Boolean var baseUrl: Text; Client: HttpClient; RequestMsg: HttpRequestMessage; ResponseMSg: HttpResponseMessage; Body: JsonObject; Content: HttpContent; ReqTxt: Text; ContentHeader: HttpHeaders; RequestHeader: HttpHeaders; begin baseUrl := 'https://ftomps64.azurewebsites.net/api/Singapur?code=GFb/v6xv9auJlEx7OOrqGhnuHYBhyoOmZV0SeGU2wezbtVsTBSQNAQ=='; RequestHeader.Clear(); RequestMsg.GetHeaders(RequestHeader); RequestHeader.Add('Accept', 'application/json'); RequestMsg.SetRequestUri(baseUrl); RequestMsg.Method('POST'); Body.Add('url', 'https://apisandbox.iras.gov.sg/iras/sb/Authentication/CorpPassAuth?callback_url=https://businesscentral.dynamics.com/sandbox&tax_agent=false&state=StanleyState&scope=GSTF7SubCP'); Body.Add('method', 'GET'); Body.Add('clientID', 'xxxx-xxxxx-xxxx-xxxx-xxxxxxxxx'); Body.Add('clientSecret', 'Ssssssssssssssssssssssssssssss'); // Write content Body.WriteTo(ReqTxt); Content.WriteFrom(ReqTxt); ContentHeader.Clear(); Content.GetHeaders(ContentHeader); ContentHeader.Remove('Content-Type'); ContentHeader.Add('Content-Type', 'application/json'); RequestMsg.Content(Content); if Client.Send(RequestMsg, ResponseMSg) then begin if ResponseMSg.IsSuccessStatusCode() then begin ResponseMSg.Content.ReadAs(ResTxt); CodigoError := ''; exit(true); end else begin CodigoError := Format(ResponseMSg.HttpStatusCode()) + ' ' + ResponseMSg.ReasonPhrase(); ResponseMSg.Content.ReadAs(ResTxt); if ResTxt = '' then ResTxt := CodigoError; exit(false); end; end else begin CodigoError := 'ERROR'; if ResponseMSg.Content.ReadAs(ResTxt) then; if ResTxt = '' then ResTxt := CodigoError; exit(false); end; end; }
And the new Azure functionusing namespace System.Net # Input bindings are passed in via param block. param($Request, $TriggerMetadata) $method = $Request.Body.method $url = $Request.Body.url $clientID = $Request.Body.clientID $clientSecret = $Request.Body.clientSecret #-------------------------------- function Execute-GetRequest #-------------------------------- ( [String] $pURL, [String] $pMethod, [String] $pClientID, [String] $pClientSecret ) { $WebRequest = [System.Net.WebRequest]::Create($pURL) $WebRequest.Headers.Add("X-IBM-Client-ID", $pClientID) $WebRequest.Headers.Add("X-IBM-Client-Secret", $pClientSecret) $WebRequest.ContentType = "application/json" $WebRequest.Method = $Method try { $resp = $WebRequest.GetResponse() $responseStream = $resp.GetResponseStream() $Reader = [System.IO.StreamReader]($responseStream) $ReturnXml = $Reader.ReadToEnd() $responseStream.Close() return $ReturnXml } catch { Write-Error $Error[0] } } $ret = Execute-GetRequest $url $method $clientID $clientSecret # Associate values to output bindings by calling 'Push-OutputBinding'. Push-OutputBinding -Name Response -Value ([HttpResponseContext]@{ StatusCode = [HttpStatusCode]::OK Body = $ret })
Regards0 -
Sorry
This line:$WebRequest.Method = $Method
Must be this one:$WebRequest.Method = $pMethod
0 -
``ñ
0 -
Thank You my updated code is here
<b> codeunit 80100 "LWA Access Token"
{
trigger OnRun()
begin
end;
procedure Refresh_LWA_Token(): Text
var
client: HttpClient;
cont: HttpContent;
header: HttpHeaders;
response: HttpResponseMessage;
Jobject: JsonObject;
tmpString: Text;
TypeHelper: Codeunit "Type Helper";
Client_ID: Text;
Client_Secret: Text;
grant_type: Text;
refresh_token: Text[10000];
ResponseText: Text;
begin
Client_ID := 'xxxxxxxxxxxxxxxx';
Client_Secret := 'xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx';
grant_type := 'refresh_token';
refresh_token := 'xxxxxxxxxxxxxxxxxxxxxxxxxxxx';
tmpString := 'client_id=' + TypeHelper.UrlEncode(Client_ID) +
'&client_secret=' + TypeHelper.UrlEncode(Client_Secret) +
'&refresh_token=' + TypeHelper.UrlEncode(refresh_token) +
'&grant_type=' + TypeHelper.UrlEncode(grant_type);
cont.WriteFrom(tmpString);
cont.ReadAs(tmpString);
cont.GetHeaders(header);
header.Add('charset', 'UTF-8');
header.Remove('Content-Type');
header.Add('Content-Type', 'application/x-www-form-urlencoded');
client.Post('https://api.amazon.com/auth/o2/token', cont, response);
response.Content.ReadAs(ResponseText);
if (response.IsSuccessStatusCode) then begin
Message(ResponseText);
end
else
Message(ResponseText);
end;
var
myInt: Integer;
} </b>
0 -
🥰 i m so happy to see my own response which helped what i was looking for, after half year 😂1
-
this was a very helpful thread!!0
Categories
- All Categories
- 73 General
- 73 Announcements
- 66.6K Microsoft Dynamics NAV
- 18.7K NAV Three Tier
- 38.4K NAV/Navision Classic Client
- 3.6K Navision Attain
- 2.4K Navision Financials
- 116 Navision DOS
- 851 Navision e-Commerce
- 1K NAV Tips & Tricks
- 772 NAV Dutch speaking only
- 617 NAV Courses, Exams & Certification
- 2K Microsoft Dynamics-Other
- 1.5K Dynamics AX
- 320 Dynamics CRM
- 111 Dynamics GP
- 10 Dynamics SL
- 1.5K Other
- 990 SQL General
- 383 SQL Performance
- 34 SQL Tips & Tricks
- 35 Design Patterns (General & Best Practices)
- 1 Architectural Patterns
- 10 Design Patterns
- 5 Implementation Patterns
- 53 3rd Party Products, Services & Events
- 1.6K General
- 1.1K General Chat
- 1.6K Website
- 83 Testing
- 1.2K Download section
- 23 How Tos section
- 252 Feedback
- 12 NAV TechDays 2013 Sessions
- 13 NAV TechDays 2012 Sessions