BC365 - Error calling Web Service

isabtogumonisabtogumon Member Posts: 49
Hello,

I have the following code and it throws an error when run, any suggestions that might help resolve the error?

Thanks in advance

Error: NavHttpClient Request Failed

Code:

codeunit 50401 ImportXMLMessage
{
procedure ImportXMLMessage()
var
SOAPAction: Record SOAPAction;
OutStrm: OutStream;
InStrm: InStream;
DialogTitle: Label 'Upload file...';
TempFileName: Text;
RequestHeaders: HttpHeaders;
RequestMessage: HttpRequestMessage;
RequestContent: HttpContent;
Client: HttpClient;
ResponseMessage: HttpResponseMessage;
Content: HttpContent;
XML: Text;

begin

Clear(SOAPAction.XMLMessage);
if not SOAPAction.XMLMessage.HasValue then begin
file.UploadIntoStream(DialogTitle, '', 'XML file(*.xml)|*.xml', TempFileName, InStrm);//then begin

Content.Clear();
RequestContent.Clear();
RequestHeaders.Clear();
RequestMessage.Content().Clear();
ResponseMessage.Content().Clear();
ResponseMessage.Headers().Clear();

RequestContent.WriteFrom(InStrm);
RequestContent.GetHeaders(RequestHeaders);


RequestHeaders.Remove('Content-Type');
RequestHeaders.Add('Content-Type', 'text/xml;charset=utf-8');
RequestHeaders.Add('SOAPAction', 'GetMovimentacao');

RequestMessage.Content := RequestContent;
RequestMessage.Method := 'POST';
RequestMessage.SetRequestUri('https://uhc.unihealth.com.br:3132/Interface.asmx');
RequestMessage.Content(RequestContent);

if Client.Send(RequestMessage, ResponseMessage) then begin
ResponseMessage.Content().ReadAs(XML);
Message(XML);
end else begin
Error('Error:\' +
'Status Code : %1\' +
'Description: %2',
ResponseMessage.HttpStatusCode,
ResponseMessage.ReasonPhrase);
end;
end;
}

Answers

  • ftorneroftornero Member Posts: 524
    Hello @isabtogumon,

    I use this procedure to call several SOAP WS and get back an XML documetnt with the response, you could try it and let me know if it works for you.
        local procedure CallPostWS(InS: InStream; URL: Text; SOAPAction: Text; var ErrorMsg: Text; var xmlDoc: XmlDocument): Boolean
        var
            Client: HttpClient;
            RequestContent: HttpContent;
            RequestHeader: HttpHeaders;
            RequestMsg: HttpRequestMessage;
            ResponseMSg: HttpResponseMessage;
            ResTxt: Text;
    
        begin
            // Add content
            RequestContent.WriteFrom(InS);
    
            // Add headers
            RequestHeader.Clear();
            RequestContent.GetHeaders(RequestHeader);
            RequestHeader.Remove('Content-Type');
            RequestHeader.Add('Content-Type', 'text/xml; charset=UTF-8');
            if SOAPAction <> '' then
                RequestHeader.Add('SOAPAction', SOAPAction);
            RequestMsg.Content(RequestContent);
    
            // Set URL and Method
            RequestMsg.SetRequestUri(URL);
            RequestMsg.Method('POST');
    
            if Client.Send(RequestMsg, ResponseMSg) then begin
                if ResponseMSg.IsSuccessStatusCode() then begin
                    ResponseMSg.Content.ReadAs(ResTxt);
                    if XmlDocument.ReadFrom(ResTxt, XMLDoc) then begin
                        ErrorMsg := '';
                        exit(true);
                    end else begin
                        ErrorMsg := ResTxt;
                        exit(false);
                    end;
                end else begin
                    ErrorMsg := ResponseMSg.ReasonPhrase();
                    exit(false);
                end;
            end else begin
                if ResponseMSg.Content.ReadAs(ResTxt) then
                    ErrorMsg := ResTxt;
                exit(false);
            end;
        end;
    

    Regards
  • isabtogumonisabtogumon Member Posts: 49
    Hello @ftornero and thanks,

    I tried with the suggested code, honestly I didn't make a lot of modifications as it is similar to mine, but it still has the same error. I'm not sure if it is presented by URL or procedure. Is it possible that you can share some code that works with real URLs so that I can see the procedure?
  • ftorneroftornero Member Posts: 524
    Hello @isabtogumon,

    You can use this site to test the SOAP WS call.

    https://www.crcind.com/csp/samples/SOAP.Demo.CLS

    Regards
  • isabtogumonisabtogumon Member Posts: 49
    Hello @ftornero and thanks again

    I must be doing something wrong since even with the test site it sends me the same error. I used the procedure below

    procedure LoadXMLandCallWS()
    var
    InStr: InStream;
    DialogTitle: Label 'Subir archivo...';
    TempFileName: Text;
    RequestHeaders: HttpHeaders;
    RequestMessage: HttpRequestMessage;
    RequestContent: HttpContent;
    Client: HttpClient;
    ResponseMessage: HttpResponseMessage;
    XML: Text;

    begin

    file.UploadIntoStream(DialogTitle, '', 'XML file(*.xml)|*.xml', TempFileName, InStr);

    RequestContent.WriteFrom(InStr);

    RequestHeaders.Clear();
    RequestContent.GetHeaders(RequestHeaders);

    RequestHeaders.Remove('Content-Type');
    RequestHeaders.Add('Content-Type', 'text/xml; charset=UTF-8');
    RequestHeaders.Add('SOAPAction', 'FindPerson');
    RequestMessage.SetRequestUri('https://www.crcind.com/csp/samples/SOAP.Demo.CLS');
    RequestMessage.Method('POST');

    if Client.Send(RequestMessage, ResponseMessage) then begin
    ResponseMessage.Content().ReadAs(XML);
    Message(XML);
    end else begin
    Error('Error: \' +
    'Status Code: %1\' +
    'Description: %2',
    ResponseMessage.HttpStatusCode,
    ResponseMessage.ReasonPhrase);
    end;
    end;
    }

    Below is stored in InStream

    <SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/&quot; xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance&quot; xmlns:s="http://www.w3.org/2001/XMLSchema"&gt;
    <SOAP-ENV:Body>
    <FindPersonResponse xmlns="http://tempuri.org"/&gt;
    </SOAP-ENV:Body>
    </SOAP-ENV:Envelope>
  • ftorneroftornero Member Posts: 524
    Hello @isabtogumon

    I have added this line to you code, marked with "// <<
    This line is missing"

    RequestMessage.Content(RequestContent);
        procedure LoadXMLandCallWS()
        var
            InStr: InStream;
            DialogTitle: Label 'Subir archivo...';
            TempFileName: Text;
            RequestHeaders: HttpHeaders;
            RequestMessage: HttpRequestMessage;
            RequestContent: HttpContent;
            Client: HttpClient;
            ResponseMessage: HttpResponseMessage;
            XML: Text;
    
        begin
    
            file.UploadIntoStream(DialogTitle, '', 'XML file(*.xml)|*.xml', TempFileName, InStr);
    
            RequestContent.WriteFrom(InStr);
    
            RequestHeaders.Clear();
            RequestContent.GetHeaders(RequestHeaders);
    
            RequestHeaders.Remove('Content-Type');
            RequestHeaders.Add('Content-Type', 'text/xml; charset=UTF-8');
            RequestHeaders.Add('SOAPAction', 'FindPerson');
            RequestMessage.SetRequestUri('https://www.crcind.com/csp/samples/SOAP.Demo.CLS');
            RequestMessage.Content(RequestContent);  // <<------ This line is missing
            RequestMessage.Method('POST');
    
            if Client.Send(RequestMessage, ResponseMessage) then begin
                ResponseMessage.Content().ReadAs(XML);
                Message(XML);
            end else begin
                Error('Error: \' +
                'Status Code: %1\' +
                'Description: %2',
                ResponseMessage.HttpStatusCode,
                ResponseMessage.ReasonPhrase);
            end;
        end;
    

    The input XML is this one:
    <soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:tem="http://tempuri.org">
       <soapenv:Header/>
       <soapenv:Body>
          <tem:FindPerson>
             <tem:id>1</tem:id>
          </tem:FindPerson>
       </soapenv:Body>
    </soapenv:Envelope>
    

    And i got this response:

    wcaa6df1mx6x.png

    Regards
  • isabtogumonisabtogumon Member Posts: 49

    Hello @ftornero

    Thanks for your reply, that fixes the problem. However, I still have not managed to make it work in the URL I want, I do not know if it is because in the content of the XML it has lines with username and password, and that in turn must be defined in the header. The content of the XML is this.

    <?xml version="1.0" encoding="utf-8"?>
    <soap:Envelope xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
      <soap:Body>
        <GetMovimentacao xmlns="http://www.unihealth.com.br/Unihealth">
          <Credencial>
            <UserName>Username</UserName>
            <Password>Password</Password>
          </Credencial>
        </GetMovimentacao>
      </soap:Body>
    </soap:Envelope>
    

    If the content of the XML has credentials, is it necessary to add something else to the structure of the procedure?
  • ftorneroftornero Member Posts: 524
    Hello @isabtogumon,

    That depends of the WS that you are calling.

    Don't you have any documentation about the WS, the WS description , a WSDL file, etc. ?

    Regards
  • isabtogumonisabtogumon Member Posts: 49
    Hi @ftornero

    I did a test in Postman and if it gave me an answer, I looked into the code options and noticed that there is a line that adds the host, I tried to add it in the structure of the AL code, however it sends an error.

    These are the lines I tried to add:
    RequestHeaders.Remove('Host');
    RequestHeaders.Add('Host', 'uhc.unihealth.com.br');
    

    This is the error that Business Central sends:
    Misused header name. Make sure request headers are used with HttpRequestMessage, response headers with HttpResponseMessage, and content headers with HttpContent objects.

    Do you know how to add the host to the header or how should I add the host?
  • ftorneroftornero Member Posts: 524
    Hello @isabtogumon,

    I don't think that header is necessary because usually is provided for the Http client and get from the URL

    j1bpvg4n2bhr.png

    The answer that you get from Postman is the expected one ?

    Regards
  • isabtogumonisabtogumon Member Posts: 49
    Thanks @ftornero for answering

    That's right, in Postman I get the correct answer
  • ftorneroftornero Member Posts: 524
    Hello @isabtogumon,

    Could you post the data with the Postman call, hiding the sensitive information ?

    Regards
  • isabtogumonisabtogumon Member Posts: 49
    Thanks @ftornero, I share the data, I hope it is what you asked me

    pe7874j6crwl.jpg

    r7773resboh6.jpg

  • ftorneroftornero Member Posts: 524
    Hello @isabtogumon,,

    Well, the XML is pretty simple and that must works from BC without any issue.

    If you want, we could connect and I take a look at your code.

    Regards
  • isabtogumonisabtogumon Member Posts: 49
    Thanks @ftornero

    If you have the opportunity to connect, I would appreciate it. Please tell me what time is convenient for you and through which tool
  • ftorneroftornero Member Posts: 524
    Hello @isabtogumon,

    I just sent to you a PM to connect with you.

    Regards
  • ftorneroftornero Member Posts: 524
    Hello @isabtogumon,

    I don't know if you have solve the conection problem with de remote WS.

    Here is some informatio about the IP range en BC SaaS

    https://docs.microsoft.com/en-us/dynamics365/business-central/dev-itpro/faq#which-ip-addresses-or-ranges-does-my-environments-api-use

    and a post about the same problem wuth the IP white list the some WS.

    https://community.dynamics.com/business/f/dynamics-365-business-central-forum/276553/can-we-set-up-a-static-ip-address-to-dynamics-365-business-central-on-cloud

    And a little App to show the client IP address:
    page 50149 "GetIP Page"
    {
        Caption = 'Get IP';
        layout
        {
    
        }
    
        actions
        {
            area(Processing)
            {
                action(GetIP)
                {
                    Promoted = true;
                    PromotedIsBig = true;
                    PromotedOnly = true;
                    Image = Line;
                    ApplicationArea = All;
                    PromotedCategory = Process;
                    Caption = 'Show IP';
                    trigger OnAction()
                    begin
                        Message('IP Address: %1', GetIPAddress());
                    end;
                }
            }
        }
    
        local procedure GetIPAddress(): Text
        var
            CodError: Text;
            ResTxt: Text;
        begin
            if CallWSRESTApi('https://naturlider2.azurewebsites.net/api/GetIP?code=WyvxPLBnJYjCqebJyFm7UYoyQAT15Zu5MOR/9AfDZQElBYCzPqtd5w==', 'GET', '', '', '', CodError, ResTxt) then
                exit(ResTxt)
            else
                Exit('IP unknown');
        end;
    
        local procedure CallWSRESTApi(apiUrl: text; method: text; contentType: Text; contentText: Text; apiKey: Text; var CodigoError: Text; var ResTxt: Text): Boolean
        var
            Client: HttpClient;
            Content: HttpContent;
            RequestMsg: HttpRequestMessage;
            RequestHeader: HttpHeaders;
            ContentHeader: HttpHeaders;
            ResponseMSg: HttpResponseMessage;
    
        begin
    
    
            if method <> 'GET' then begin
                // Write content
                Content.Clear();
                if StrLen(contentText) > 0 then
                    Content.WriteFrom(contentText);
                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);
            RequestMsg.SetRequestUri(apiUrl);
            RequestMsg.Method(method);
    
            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;
                    if (method = 'DELETE') and (ResponseMSg.HttpStatusCode = 404) then
                        exit(true)
                    else
                        exit(false);
                end;
            end else begin
                CodigoError := 'ERROR';
                if ResponseMSg.Content.ReadAs(ResTxt) then;
                if ResTxt = '' then
                    ResTxt := CodigoError;
                exit(false);
            end;
        end;
    }
    

    Regards
  • isabtogumonisabtogumon Member Posts: 49
    Hello @ftornero

    I have already confirmed the reason for the error, this is due to the restriction of the server where the web service is hosted. I tried the code in the locale and it worked fine there, I got the expected result. Regarding access for cloud environment, IP range will be whitelist, I hope that will solve the problem.

    I did test the code to get the IP address, but from what I read in the link, BC's IP is dynamic so if it is whitelist the IP might change and an error would occur.

Sign In or Register to comment.