Today I would like to discuss with you how to implement a Web service for Dynamics NAV and to interact with this service from Dynamics NAV C/SIDE Client or even web server.
HandleRequest()
XMLDocInstruct := XMLOutG.createProcessingInstruction('xml','version="1.0" encoding="UTF-8" standalone="no"');
XMLOutG.insertBefore(XMLDocInstruct,XMLOutG.childNodes.item(0));
DocResponseRoot := XMLOutG.createElement('Response');
My target was to build a NAV database (because employees are used to work with it's interface) with online access. In this online database additional information about customers (such as bonus balance) should be stored. Information about customer bonus balance should be available not only in NAV working database, but in internet, too. Further more, when posting a discount in NAV operational database, customer bonus balance should be reduced.
From the system point of view, the Navision Application Server, connected to bonus database, should be started. Certain TCP ports should be opened. When the message come to this port some event should be triggered, and a response should be posted back. The language for this interaction should be XML.
Codeunit 1, Trigger 99 NASHandler(ATASID : Text[260])
GenSetupL.GET;
GenSetupL.TESTFIELD("NAS Application Port");
IF ISCLEAR(CC) THEN CREATE(CC);
IF ISCLEAR(SBA) THEN CREATE(SBA);
CC.AddBusAdapter(SBA, 0);
SBA.OpenSocket(GenSetupL."NAS Application Port", '');
MESSAGE(StartedNASTxt, GenSetupL."NAS Application Port");
where
CC Automation 'Navision Communication Component version 2'.CommunicationComponent
SBA Automation 'Navision Socket Bus Adapter'.SocketBusAdapter are the global variables. After adding CC variables the new trigger was created automaticly:
CC::MessageReceived(VAR InMessage : Automation "''.IDISPATCH")
From the system point of view, the Navision Application Server, connected to bonus database, should be started. Certain TCP ports should be opened. When the message come to this port some event should be triggered, and a response should be posted back. The language for this interaction should be XML.
Codeunit 1, Trigger 99 NASHandler(ATASID : Text[260])
GenSetupL.GET;
GenSetupL.TESTFIELD("NAS Application Port");
IF ISCLEAR(CC) THEN CREATE(CC);
IF ISCLEAR(SBA) THEN CREATE(SBA);
CC.AddBusAdapter(SBA, 0);
SBA.OpenSocket(GenSetupL."NAS Application Port", '');
MESSAGE(StartedNASTxt, GenSetupL."NAS Application Port");
where
CC Automation 'Navision Communication Component version 2'.CommunicationComponent
SBA Automation 'Navision Socket Bus Adapter'.SocketBusAdapter are the global variables. After adding CC variables the new trigger was created automaticly:
CC::MessageReceived(VAR InMessage : Automation "''.IDISPATCH")
Then the new trigger for proceeding incoming request was created and the next code was added:
HandleRequest()
XMLDocInstruct := XMLOutG.createProcessingInstruction('xml','version="1.0" encoding="UTF-8" standalone="no"');
XMLOutG.insertBefore(XMLDocInstruct,XMLOutG.childNodes.item(0));
DocResponseRoot := XMLOutG.createElement('Response');
XMLOutG.documentElement := DocResponseRoot;
DocRoot:=XMLInG.selectSingleNode('//Request');
IF ISCLEAR(DocRoot) THEN BEGIN
AddSubNodeText(DocResponseRoot, XMLNode, 'Error', MethodNotProvided);
EXIT;
END;
AddSubNodeText(DocResponseRoot, XMLNode, 'Error', MethodNotProvided);
EXIT;
END;
Method := GetNamedAttributeText(DocRoot,'method');
CASE Method OF
'GetBalance':
BEGIN
XMLNode := DocRoot.selectSingleNode('CardID');
IF ISCLEAR(XMLNode) THEN BEGIN
AddSubNodeText(DocResponseRoot, XMLNode, 'Error', ParametersNotProvided);
EXIT;
END;
'GetBalance':
BEGIN
XMLNode := DocRoot.selectSingleNode('CardID');
IF ISCLEAR(XMLNode) THEN BEGIN
AddSubNodeText(DocResponseRoot, XMLNode, 'Error', ParametersNotProvided);
EXIT;
END;
CalculateCardBalance(CardL.ID, TotalBalanceL);
AddSubNodeText(DocResponseRoot, XMLNode, 'TotalBalance', FORMAT(TotalBalanceL,0,9));
END;
// ... other method proceeding goes here
END;
A start point for receiving incoming message is here:
AddSubNodeText(DocResponseRoot, XMLNode, 'TotalBalance', FORMAT(TotalBalanceL,0,9));
END;
// ... other method proceeding goes here
END;
A start point for receiving incoming message is here:
CC::MessageReceived(VAR InMessage : Automation "''.IDISPATCH")
CLEAR(XMLOutG);
IF ISCLEAR(XMLInG) THEN
CREATE(XMLInG);
IF ISCLEAR(XMLOutG) THEN
CREATE(XMLOutG);
XMLOutG.async := FALSE;
ComIn := InMessage;
InStrm := ComIn.GetStream;
Found := FALSE;
IF ISCLEAR(XMLInG) THEN
CREATE(XMLInG);
IF ISCLEAR(XMLOutG) THEN
CREATE(XMLOutG);
XMLOutG.async := FALSE;
ComIn := InMessage;
InStrm := ComIn.GetStream;
Found := FALSE;
REPEAT
IF InStrm.READTEXT(Txt) = 0 THEN Found := TRUE;
UNTIL InStrm.EOS OR Found;
XMLInG.load(InStrm);
ComOut := ComIn.CreateReply();
OutStrm := ComOut.GetStream();
HandleRequest();
OutStrm.WRITETEXT('HTTP/1.1 200 OK');
OutStrm.WRITETEXT();
OutStrm.WRITETEXT('Content-Type: text/xml; charset=utf-8');
OutStrm.WRITETEXT();
TempFile := ENVIRON('TEMP') + '\bonus_out.xml';
XMLOutG.save(TempFile);
F.OPEN(TempFile);
OutStrm.WRITETEXT('Content-Length: ' + FORMAT(F.LEN));
F.CLOSE;
OutStrm.WRITETEXT();
OutStrm.WRITETEXT();
XMLOutG.save(OutStrm);
ComOut.Send(0);
ComIn.CommitMessage;
Some comments.
IF InStrm.READTEXT(Txt) = 0 THEN Found := TRUE;
UNTIL InStrm.EOS OR Found;
XMLInG.load(InStrm);
ComOut := ComIn.CreateReply();
OutStrm := ComOut.GetStream();
HandleRequest();
OutStrm.WRITETEXT('HTTP/1.1 200 OK');
OutStrm.WRITETEXT();
OutStrm.WRITETEXT('Content-Type: text/xml; charset=utf-8');
OutStrm.WRITETEXT();
TempFile := ENVIRON('TEMP') + '\bonus_out.xml';
XMLOutG.save(TempFile);
F.OPEN(TempFile);
OutStrm.WRITETEXT('Content-Length: ' + FORMAT(F.LEN));
F.CLOSE;
OutStrm.WRITETEXT();
OutStrm.WRITETEXT();
XMLOutG.save(OutStrm);
ComOut.Send(0);
ComIn.CommitMessage;
Some comments.
- The incoming stream is used to read message from the 0-character, because there are some headers at the top of it.
- As we could see when sending HTTP response we should indicate the length of the response. That's why we should save the XML response to the temporary file, open it as a stream and read the length of it.
Now, we are ready to implement some modification in the client database.
No comments:
Post a Comment