如何在 Progress 4GL 中发送 SOAP headers

How to send a SOAP headers in Progress 4GL

你好 Progress 4GL 开发者,

我想在 ABL session 中使用 UPS 跟踪 API。
我有 运行 用于 UPS WSDL 的 WSDLAnalyser 并遵循了文档。

这是我希望发送的 XML 中的请求示例(包括 header):

<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:v1="http://www.ups.com/XMLSchema/XOLTWS/UPSS/v1.0" xmlns:v2="http://www.ups.com/XMLSchema/XOLTWS/Track/v2.0" xmlns:v11="http://www.ups.com/XMLSchema/XOLTWS/Common/v1.0">
<soapenv:Header>
   <v1:UPSSecurity>
      <v1:UsernameToken>
         <v1:Username>MYUSERNAME</v1:Username>
         <v1:Password>MYPASSWORD</v1:Password>
      </v1:UsernameToken>
      <v1:ServiceAccessToken>
         <v1:AccessLicenseNumber>MYLICENSENUMBER</v1:AccessLicenseNumber>
      </v1:ServiceAccessToken>
   </v1:UPSSecurity>
</soapenv:Header>
<soapenv:Body>
   <v2:TrackRequest>
      <v2:ReferenceNumber>
         <v2:Code>MYCODE</v2:Code>
         <v2:Value>MYORDERNUM</v2:Value>
      </v2:ReferenceNumber>
   </v2:TrackRequest>
</soapenv:Body>
</soapenv:Envelope>

此请求在使用 SOAPUI 发送以测试请求时按预期工作。 到目前为止,这是我尝试发出相同请求的 Progress4GL 代码:

{us/mf/mfdtitle.i}
{/qond/apps/mfgpro/customizations/mfg/work/src/us/xx/xxwhsxml.i}

define variable hs as handle no-undo.
define variable hp as handle no-undo.
DEFINE VARIABLE cBody  AS LONGCHAR NO-UNDO.
DEFINE VARIABLE cBody2 AS LONGCHAR NO-UNDO.

cBody = '<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:v1="http://www.ups.com/XMLSchema/XOLTWS/UPSS/v1.0" xmlns:v2="http://www.ups.com/XMLSchema/XOLTWS/Track/v2.0" xmlns:v11="http://www.ups.com/XMLSchema/XOLTWS/Common/v1.0">
<soapenv:Header>
   <v1:UPSSecurity>
      <v1:UsernameToken>
         <v1:Username>MYUSERNAME</v1:Username>
         <v1:Password>MYPASSWORD</v1:Password>
      </v1:UsernameToken>
      <v1:ServiceAccessToken>
         <v1:AccessLicenseNumber>MYLICENSENUMBER</v1:AccessLicenseNumber>
      </v1:ServiceAccessToken>
   </v1:UPSSecurity>
</soapenv:Header>
<soapenv:Body>
   <v2:TrackRequest>
      <v2:ReferenceNumber>
         <v2:Code>MYCODE</v2:Code>
         <v2:Value>MYORDERNUM</v2:Value>
      </v2:ReferenceNumber>
   </v2:TrackRequest>
</soapenv:Body>
</soapenv:Envelope>'.

create server hs.
hs:connect( "-WSDL /home/jbetts/track/Track.wsdl -Port TrackPort -TargetNamespace http://www.ups.com/WSDL/XOLTWS/Track/v2.0 -nohostverify -nosessionreuse" ). 

run TrackPortType set hp on server hs.
run ProcessTrack in hp ( input cBody, output cBody2 ).

delete procedure hp.
hs:disconnect().
delete object hs.

我认为问题是当我 运行 ProcessTrack 程序(使用 SET-CALLBACK-PROCEDURE)时我需要以某种方式发送 header 但我不确定如何.

提前致谢!

SET-CALLBACK-PROCEDURE 是方法。

在 运行 SOAP 调用之前,您需要执行类似的操作(setRequestSessionHandler 是过程的名称,因此它可以是任何您想要的):

hp:SET-CALLBACK-PROCEDURE("REQUEST-HEADER", "setRequestSessionHeader").

request-callback 有两个输入和两个输出参数(名称取自文档,只要 input/output 和数据类型正确就可以任意调用):

DEFINE OUTPUT PARAMETER hSOAPHeader         AS HANDLE    NO-UNDO. 
DEFINE INPUT  PARAMETER cOperationNamespace AS CHARACTER NO-UNDO.  
DEFINE INPUT  PARAMETER cOperationLocalName AS CHARACTER NO-UNDO.  
DEFINE OUTPUT PARAMETER plDeleteOnDone AS LOGICAL   NO-UNDO. 

hSOAPHeader — A handle to a SOAP header object that encapsulates the header of the SOAP message that is about to be sent (request header) or that has just been received (response header). In a response header handler, the SOAP header object has no content if the NUM-HEADER-ENTRIES attribute on the object handle returns the value 0; otherwise it contains one or more SOAP header entries. In a request header handler, this is an OUTPUT parameter, therefore if the outgoing SOAP message requires a SOAP header, you must either build a SOAP header for this parameter to reference or provide an existing SOAP header saved from a previous response callback.

cOperationNamespace — Contains the namespace portion of the operation's qualified name. Use this parameter together with the cOperationLocalName parameter if you need to identify the operation for which the SOAP message is being sent or received.

cOperationLocalName — Contains the local-name portion of the operation's qualified name. Use this parameter together with the cOperationNamespace parameter if you need to identify the operation for which the SOAP message is being sent or received.

lDeleteOnDone — (Request callback only) Tells OpenEdge to delete the SOAP header object and all of the parsed XML after the SOAP header has been inserted into the out-bound SOAP message

这是我模仿 WS-Security 所做的,不完全是您需要的,而且代码也很多。您应该能够 "mine it" 满足您的任何需求。

PROCEDURE setRequestSessionHeader :
/*------------------------------------------------------------------------------
  Purpose:     
  Parameters:  <none>
  Notes:       
------------------------------------------------------------------------------*/

DEFINE OUTPUT PARAMETER phHeader       AS HANDLE    NO-UNDO. 
DEFINE INPUT  PARAMETER pcNamespace    AS CHARACTER NO-UNDO.  
DEFINE INPUT  PARAMETER pcLocalNS      AS CHARACTER NO-UNDO.  
DEFINE OUTPUT PARAMETER plDeleteOnDone AS LOGICAL   NO-UNDO. 

DEFINE VARIABLE hSoapHeaderEntryref1    AS HANDLE      NO-UNDO.
DEFINE VARIABLE hSoapHeaderEntryref2    AS HANDLE      NO-UNDO.
DEFINE VARIABLE hSoapHeaderEntryref3    AS HANDLE      NO-UNDO.
DEFINE VARIABLE hSoapHeaderEntryref4    AS HANDLE      NO-UNDO.
DEFINE VARIABLE hSoapHeaderEntryref5    AS HANDLE      NO-UNDO.
DEFINE VARIABLE hXDocument              AS HANDLE      NO-UNDO.
DEFINE VARIABLE hOASSecurity            AS HANDLE      NO-UNDO.
DEFINE VARIABLE hOASUsernameToken       AS HANDLE      NO-UNDO.
DEFINE VARIABLE hOASUserName            AS HANDLE      NO-UNDO.
DEFINE VARIABLE hOASPassword            AS HANDLE      NO-UNDO.
DEFINE VARIABLE hOASPasswordType        AS HANDLE      NO-UNDO.
DEFINE VARIABLE hOASNonce               AS HANDLE      NO-UNDO.
DEFINE VARIABLE hWSUCreated             AS HANDLE      NO-UNDO.
DEFINE VARIABLE hADDMessageID           AS HANDLE      NO-UNDO.
DEFINE VARIABLE hADDTo                  AS HANDLE      NO-UNDO.
DEFINE VARIABLE hADDAction              AS HANDLE      NO-UNDO.
DEFINE VARIABLE hAMA_SecurityHostedUser AS HANDLE      NO-UNDO.
DEFINE VARIABLE hUserId                 AS HANDLE      NO-UNDO.
DEFINE VARIABLE hTxt                    AS HANDLE      NO-UNDO.
DEFINE VARIABLE hAttr                   AS HANDLE      NO-UNDO.
DEFINE VARIABLE hRootNode               AS HANDLE      NO-UNDO.

/* Namespaces */
DEFINE VARIABLE cNSAddressing           AS CHARACTER   NO-UNDO.
DEFINE VARIABLE cNSSecurity             AS CHARACTER   NO-UNDO.
DEFINE VARIABLE cNSSecurityUtils        AS CHARACTER   NO-UNDO.
DEFINE VARIABLE cNSAmaSec               AS CHARACTER   NO-UNDO.
DEFINE VARIABLE cMessageId              AS CHARACTER   NO-UNDO.
DEFINE VARIABLE cUserName               AS CHARACTER   NO-UNDO.
DEFINE VARIABLE cPasswordClear          AS CHARACTER   NO-UNDO.
DEFINE VARIABLE cPasswordDigest         AS CHARACTER   NO-UNDO.
DEFINE VARIABLE cCreated                AS CHARACTER   NO-UNDO.
DEFINE VARIABLE cAction                 AS CHARACTER   NO-UNDO.
DEFINE VARIABLE cTo                     AS CHARACTER   NO-UNDO.
DEFINE VARIABLE cNonceB64               AS CHARACTER   NO-UNDO.
DEFINE VARIABLE cOfficeId               AS CHARACTER   NO-UNDO.
DEFINE VARIABLE mNonce                  AS MEMPTR      NO-UNDO.

ASSIGN 
    cNSAddressing     = "http://www.w3.org/2005/08/addressing"
    cNSSecurity       = "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd"
    cNSSecurityUtils  = "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd"
    cNSAmaSec         = "http://illkeepthisasecret..."
    cAction           = "http://illkeepthisasecret..."
    cTo               = "https://noded1.production.webservices.amadeus.com/1ASIWIBNAIT".

ASSIGN
    cUserName         = "usernamegoeshere"
    cOfficeId         = "anothersettinggoeshere"
    cPasswordClear    = "passwordgoeshere".

/* Delete header when done! */
ASSIGN 
    plDeleteOnDone = TRUE.

CREATE SOAP-HEADER ghSoapHeader.
CREATE SOAP-HEADER-ENTRYREF hSoapHeaderEntryref1.
CREATE SOAP-HEADER-ENTRYREF hSoapHeaderEntryref2.
CREATE SOAP-HEADER-ENTRYREF hSoapHeaderEntryref3.
CREATE SOAP-HEADER-ENTRYREF hSoapHeaderEntryref4.
CREATE SOAP-HEADER-ENTRYREF hSoapHeaderEntryref5.


CREATE X-DOCUMENT hXDocument.
CREATE X-NODEREF hRootNode.
CREATE X-NODEREF hOASSecurity.
CREATE X-NODEREF hTxt.
CREATE X-NODEREF hOASUsernameToken.
CREATE X-NODEREF hOASPassword.
CREATE X-NODEREF hOASUserName.
CREATE X-NODEREF hOASNonce.
CREATE X-NODEREF hADDMessageID.
CREATE X-NODEREF hADDTo.
CREATE X-NODEREF hADDAction.
CREATE X-NODEREF hWSUCreated.
CREATE X-NODEREF hAMA_SecurityHostedUser.
CREATE X-NODEREF hUserId.


/* Not DATETIME-TZ! */
DEFINE VARIABLE dtZuluNow AS DATETIME    NO-UNDO.

/* Genereate a random key and base64-encode it */
SET-SIZE(mNonce) = 16.

mNonce = GENERATE-RANDOM-KEY.

cNonceB64 = BASE64-ENCODE(mNonce).

/* Get time in UTC/GMT/ZULU/Timezone 0 */
dtZuluNow = DATETIME-TZ(NOW,0).

/* Manipulate the date string to fit specs... */
ASSIGN 
    cMessageId = LC(SUBSTRING(STRING(GENERATE-UUID), 8, 20))
    cCreated = STRING(dtZuluNow, "9999-99-99THH:MM:SS") + ":000Z".
/* This is internal stuff for generating a digest... */
RUN generatePasswordDigest( mNonce, cCreated, cPasswordClear, OUTPUT cPasswordDigest).

/* Root node */
hXDocument:CREATE-NODE-NAMESPACE(hRootNode, "", "root", "element").
hXDocument:INSERT-BEFORE(hRootNode, ?).

/**** Addressing ****/
/* MessageID */
ghSoapHeader:ADD-HEADER-ENTRY(hSoapHeaderEntryref1).
hXDocument:CREATE-NODE-NAMESPACE(hADDMessageID, cNSAddressing, "MessageID", "ELEMENT").
hRootNode:APPEND-CHILD(hAddMessageID).
hXDocument:CREATE-NODE(hTxt, "", "TEXT").
hTxt:NODE-VALUE = cMessageId.
hADDMessageId:APPEND-CHILD(hTxt).
hSoapHeaderEntryref1:SET-NODE(hADDMessageID).

/* Action */
ghSoapHeader:ADD-HEADER-ENTRY(hSoapHeaderEntryref2).
hXDocument:CREATE-NODE-NAMESPACE(hADDAction, cNSAddressing, "Action", "ELEMENT").
hRootNode:APPEND-CHILD(hADDAction).
hXDocument:CREATE-NODE(hTxt, "", "TEXT").
hTxt:NODE-VALUE = cAction.
hADDAction:APPEND-CHILD(hTxt).
hSoapHeaderEntryref2:SET-NODE(hADDAction).

/* To */
ghSoapHeader:ADD-HEADER-ENTRY(hSoapHeaderEntryref3).
hXDocument:CREATE-NODE-NAMESPACE(hADDTo, cNSAddressing, "To", "ELEMENT").
hRootNode:APPEND-CHILD(hADDTo).
hXDocument:CREATE-NODE(hTxt, "", "TEXT").
hTxt:NODE-VALUE = cTo.
hADDTo:APPEND-CHILD(hTxt).
hSoapHeaderEntryref3:SET-NODE(hADDTo).

/**** Security ****/
/* Root node */
ghSoapHeader:ADD-HEADER-ENTRY(hSoapHeaderEntryref4).
hXDocument:CREATE-NODE-NAMESPACE(hOASSecurity, cNSSecurity, "Security", "ELEMENT").
hRootNode:APPEND-CHILD(hOASSecurity).

/* UserNameToken node */
hXDocument:CREATE-NODE-NAMESPACE(hOASUsernameToken, cNSSecurity, "UsernameToken", "ELEMENT").
hOASUsernameToken:SET-ATTRIBUTE("Id", "UsernameToken-1").
hOASSecurity:INSERT-BEFORE(hOASUsernameToken, ?).

/* Username */
hXDocument:CREATE-NODE-NAMESPACE(hOASUserName, cNSSecurity, "Username", "ELEMENT").
hRootNode:APPEND-CHILD(hOASUserName).
hXDocument:CREATE-NODE(hTxt, "", "TEXT").
hTxt:NODE-VALUE = cUserName.
hOASUserName:APPEND-CHILD(hTxt).
hOASUsernameToken:APPEND-CHILD(hOASUserName).

/* Nonce */
hXDocument:CREATE-NODE-NAMESPACE(hOASNonce, cNSSecurity, "Nonce", "ELEMENT").
hOASNonce:SET-ATTRIBUTE("EncodingType", "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-soap-message-security-1.0#Base64Binary").
hOASUsernameToken:APPEND-CHILD(hOASNonce).
hXDocument:CREATE-NODE(hTxt, "", "TEXT").
hTxt:NODE-VALUE = cNonceB64.
hOASNonce:APPEND-CHILD(hTxt).

/* Password hash */
hXDocument:CREATE-NODE-NAMESPACE(hOASPassword, cNSSecurity, "Password", "ELEMENT").
hOASPassword:SET-ATTRIBUTE("Type", "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-username-token-profile-1.0#PasswordDigest").
hOASUsernameToken:APPEND-CHILD(hOASPassword).
hXDocument:CREATE-NODE(hTxt, "", "TEXT").
hTxt:NODE-VALUE = cPasswordDigest.
hOASPassword:APPEND-CHILD(hTxt).

/* Created - timestamp */
hXDocument:CREATE-NODE-NAMESPACE(hWSUCreated, cNSSecurityUtils, "Created", "ELEMENT").
hOASUsernameToken:APPEND-CHILD(hWSUCreated).
hXDocument:CREATE-NODE(hTxt, "", "TEXT").
hTxt:NODE-VALUE = cCreated.
hWSUCreated:APPEND-CHILD(hTxt).
hSoapHeaderEntryref4:SET-NODE(hOASSecurity).

/**** AMA_SecurityHostedUser ****/
/* Root node */
ghSoapHeader:ADD-HEADER-ENTRY(hSoapHeaderEntryref5).
hXDocument:CREATE-NODE-NAMESPACE(hAMA_SecurityHostedUser, cNSAMASec, "AMA_SecurityHostedUser", "ELEMENT").
hRootNode:APPEND-CHILD(hAMA_SecurityHostedUser).


/* UserID */
hXDocument:CREATE-NODE-NAMESPACE(hUserID, cNSAMASec, "UserID", "ELEMENT").

hUserID:SET-ATTRIBUTE("AgentDutyCode", "SU"). 
hUserID:SET-ATTRIBUTE("RequestorType", "U").
hUserID:SET-ATTRIBUTE("PseudoCityCode", cOfficeId).
hUserID:SET-ATTRIBUTE("POS_Type", "1").

hAMA_SecurityHostedUser:APPEND-CHILD(hUserID).
hSoapHeaderEntryref5:SET-NODE(hAMA_SecurityHostedUser).

/* Output the header */
phHeader = ghSoapHeader.

/* Cleanup */
IF VALID-HANDLE(hOASSecurity) THEN 
    DELETE OBJECT hOASSecurity.
IF VALID-HANDLE(hOASUsernameToken) THEN
    DELETE OBJECT hOASUsernameToken.
IF VALID-HANDLE(hOASUserName) THEN
    DELETE OBJECT hOASUserName.
IF VALID-HANDLE(hOASPassword) THEN
    DELETE OBJECT hOASPassword.
IF VALID-HANDLE(hADDMessageID) THEN
    DELETE OBJECT hADDMessageID.  
IF VALID-HANDLE(hOASNonce) THEN
    DELETE OBJECT hOASNonce.
IF VALID-HANDLE(hTxt) THEN
    DELETE OBJECT hTxt.
IF VALID-HANDLE(hADDTo) THEN
    DELETE OBJECT hADDTo.    
IF VALID-HANDLE(hWSUCreated) THEN
    DELETE OBJECT hWSUCreated.
IF VALID-HANDLE(hADDAction) THEN
    DELETE OBJECT hADDAction.     
IF VALID-HANDLE(hXDocument) THEN
    DELETE OBJECT hXDocument.
IF VALID-HANDLE(hRootNode) THEN
    DELETE OBJECT hRootNode.
IF VALID-HANDLE(hSoapHeaderEntryRef1) THEN
    DELETE OBJECT hSoapHeaderEntryRef1.
IF VALID-HANDLE(hSoapHeaderEntryRef2) THEN
    DELETE OBJECT hSoapHeaderEntryRef2.
IF VALID-HANDLE(hSoapHeaderEntryRef3) THEN
    DELETE OBJECT hSoapHeaderEntryRef3.
IF VALID-HANDLE(hSoapHeaderEntryRef4) THEN
    DELETE OBJECT hSoapHeaderEntryRef4.
IF VALID-HANDLE(hSoapHeaderEntryRef5) THEN
    DELETE OBJECT hSoapHeaderEntryRef5.
IF VALID-HANDLE(hAMA_SecurityHostedUser) THEN
    DELETE OBJECT hAMA_SecurityHostedUser.
IF VALID-HANDLE(hUserId) THEN
    DELETE OBJECT hUserId.

END.