Sample code for 30+ languages & platforms
PureBasic

SharePoint -- Get Server Form Digest Value

Demonstrates how to get a server form digest value to be placed in the X-RequestDigest HTTP request header for POST, PUT, MERGE, and DELETE requests. A form digest value is typically valid for 1800 seconds (i.e. 30 minutes). This example persists the value to a file, and only requests a new form digest value if the existing one is near expiration.

Chilkat PureBasic Downloads

PureBasic
IncludeFile "CkHttp.pb"
IncludeFile "CkXml.pb"
IncludeFile "CkHttpResponse.pb"
IncludeFile "CkFileAccess.pb"
IncludeFile "CkDateTime.pb"

Procedure ChilkatExample()

    success.i = 0

    ; This requires the Chilkat API to have been previously unlocked.
    ; See Global Unlock Sample for sample code.

    ; First, let's see if we already have a persisted form digest value
    ; that hasn't yet expired.
    fac.i = CkFileAccess::ckCreate()
    If fac.i = 0
        Debug "Failed to create object."
        ProcedureReturn
    EndIf

    xml.i = CkXml::ckCreate()
    If xml.i = 0
        Debug "Failed to create object."
        ProcedureReturn
    EndIf

    ; My example code (below) persists the form digest XML in this format:
    ; 
    ; 	<savedFormDigestValue>
    ; 	<d:ExpireDateTime>2017-04-12T20:46:39Z</d:ExpireDateTime>
    ; 	<d:FormDigestValue>0x3059FFB920651834540F3E6792EA73F5746B302E953FF4E808E485DB1E6C2836C7CF924644995F092453B02A94DE14A7962674B7B16780AF16EAFB8C246BCDC7,12 Apr 2017 17:08:22 -0000</d:FormDigestValue>
    ; 	</savedFormDigestValue>
    ; 

    dtExpire.i = CkDateTime::ckCreate()
    If dtExpire.i = 0
        Debug "Failed to create object."
        ProcedureReturn
    EndIf

    dtNow.i = CkDateTime::ckCreate()
    If dtNow.i = 0
        Debug "Failed to create object."
        ProcedureReturn
    EndIf

    formDigestXmlFile.s = "qa_data/sharepoint/savedFormDigestValue.xml"
    If CkFileAccess::ckFileExists(fac,formDigestXmlFile) = 1

        CkXml::ckLoadXmlFile(xml,formDigestXmlFile)

        ; Get the expire date/time
        CkDateTime::ckSetFromTimestamp(dtExpire,CkXml::ckGetChildContent(xml,"d:ExpireDateTime"))

        ; Get the current date/time
        CkDateTime::ckSetFromCurrentSystemTime(dtNow)

        ; Get both times as Unix time values
        tNow.i = CkDateTime::ckGetAsUnixTime(dtNow,0)
        tExpire.i = CkDateTime::ckGetAsUnixTime(dtExpire,0)

        ; If tNow >= tExpire, then fall through.
        ; Otherwise, just use the cached digest value.
        If tNow < tExpire
            Debug "Cached digest value is not yet expired."
            Debug "X-RequestDigest: " + CkXml::ckGetChildContent(xml,"d:FormDigestValue")
            CkFileAccess::ckDispose(fac)
            CkXml::ckDispose(xml)
            CkDateTime::ckDispose(dtExpire)
            CkDateTime::ckDispose(dtNow)
            ProcedureReturn
        EndIf

    EndIf

    ; If we got to this point, the cached digest value either does not exist, or expired.

    http.i = CkHttp::ckCreate()
    If http.i = 0
        Debug "Failed to create object."
        ProcedureReturn
    EndIf

    ; If SharePoint Windows classic authentication is used, then set the 
    ; Login, Password, LoginDomain, and NtlmAuth properties.
    CkHttp::setCkLogin(http, "SHAREPOINT_USERNAME")
    CkHttp::setCkPassword(http, "SHAREPOINT_PASSWORD")
    CkHttp::setCkLoginDomain(http, "SHAREPOINT_NTLM_DOMAIN")
    CkHttp::setCkNtlmAuth(http, 1)

    ; The more common case is to use SharePoint Online authentication (via the SPOIDCRL cookie).
    ; If so, do not set Login, Password, LoginDomain, and NtlmAuth, and instead
    ; establish the cookie as shown at SharePoint Online Authentication

    ; When creating, updating, and deleting SharePoint entities, we'll need
    ; to first get the server's form digest value to send in the X-RequestDigest header.
    ; This can be retrieved by making a POST request with an empty body to
    ; http://<site url>/_api/contextinfo and extracting the value of the
    ; d:FormDigestValue node in the XML that the contextinfo endpoint returns.

    ; Apparently, SharePoint needs an "Accept" request header equal to "application/xml",
    ; otherwise SharePoint will return an utterly incomprehensible and useless error message.
    savedAccept.s = CkHttp::ckAccept(http)
    CkHttp::setCkAccept(http, "application/xml")

    ; Note: The last argument ("utf-8") is meaningless here because the body is empty.
    resp.i = CkHttpResponse::ckCreate()
    If resp.i = 0
        Debug "Failed to create object."
        ProcedureReturn
    EndIf

    success = CkHttp::ckHttpStr(http,"POST","https://SHAREPOINT_HTTPS_DOMAIN/_api/contextinfo","","utf-8","application/xml",resp)
    If success = 0
        Debug CkHttp::ckLastErrorText(http)
        CkFileAccess::ckDispose(fac)
        CkXml::ckDispose(xml)
        CkDateTime::ckDispose(dtExpire)
        CkDateTime::ckDispose(dtNow)
        CkHttp::ckDispose(http)
        CkHttpResponse::ckDispose(resp)
        ProcedureReturn
    EndIf

    ; Restore the default Accept header
    CkHttp::setCkAccept(http, savedAccept)

    If CkHttpResponse::ckStatusCode(resp) <> 200
        ; A response status code not equal to 200 indicates failure.
        Debug "Response status code = " + Str(CkHttpResponse::ckStatusCode(resp))
        Debug "Response body:"
        Debug CkHttpResponse::ckBodyStr(resp)
        CkFileAccess::ckDispose(fac)
        CkXml::ckDispose(xml)
        CkDateTime::ckDispose(dtExpire)
        CkDateTime::ckDispose(dtNow)
        CkHttp::ckDispose(http)
        CkHttpResponse::ckDispose(resp)
        ProcedureReturn
    EndIf

    CkXml::ckLoadXml(xml,CkHttpResponse::ckBodyStr(resp))

    ; The response XML looks like this:

    ; <?xml version="1.0" encoding="utf-8" ?>
    ; <d:GetContextWebInformation xmlns:d="http://schemas.microsoft.com/ado/2007/08/dataservices" xmlns:m="http://schemas.microsoft.com/ado/2007/08/dataservices/metadata" xmlns:georss="http://www.georss.org/georss" xmlns:gml="http://www.opengis.net/gml" m:type="SP.ContextWebInformation">
    ;     <d:FormDigestTimeoutSeconds m:type="Edm.Int32">1800</d:FormDigestTimeoutSeconds>
    ;     <d:FormDigestValue>0x3059FFB920651834540F3E6792EA73F5746B302E953FF4E808E485DB1E6C2836C7CF924644995F092453B02A94DE14A7962674B7B16780AF16EAFB8C246BCDC7,12 Apr 2017 17:08:22 -0000</d:FormDigestValue>
    ;     <d:LibraryVersion>15.0.4569.1000</d:LibraryVersion>
    ;     <d:SiteFullUrl>https://SHAREPOINT_HTTPS_DOMAIN</d:SiteFullUrl>
    ;     <d:SupportedSchemaVersions m:type="Collection(Edm.String)">
    ;         <d:element>14.0.0.0</d:element>
    ;         <d:element>15.0.0.0</d:element>
    ;     </d:SupportedSchemaVersions>
    ;     <d:WebFullUrl>https://SHAREPOINT_HTTPS_DOMAIN</d:WebFullUrl>
    ; </d:GetContextWebInformation>
    ; 

    ; Cache the digest value, and also an expiration time.  If this code is run again
    ; before the digest expires, we'll just get it from the file.
    xml2.i = CkXml::ckCreate()
    If xml2.i = 0
        Debug "Failed to create object."
        ProcedureReturn
    EndIf

    CkXml::setCkTag(xml2, "savedFormDigestValue")
    CkXml::ckNewChild2(xml2,"d:FormDigestValue",CkXml::ckGetChildContent(xml,"d:FormDigestValue"))

    timeoutInSec.i = CkXml::ckGetChildIntValue(xml,"d:FormDigestTimeoutSeconds")
    Debug "Timeout in seconds = " + Str(timeoutInSec)

    ; Convert this to an expire timestamp.
    ; Let's make it expire 30 seconds prior to the actual timeout, just to be safe.
    If timeoutInSec > 30
        timeoutInSec = timeoutInSec - 30
    EndIf

    CkDateTime::ckSetFromCurrentSystemTime(dtExpire)
    CkDateTime::ckAddSeconds(dtExpire,timeoutInSec)
    CkXml::ckNewChild2(xml2,"d:ExpireDateTime",CkDateTime::ckGetAsTimestamp(dtExpire,0))

    ; Persist the digest and expire time to a file.
    CkXml::ckSaveXml(xml2,formDigestXmlFile)

    Debug "Here is the new form digest value:"
    Debug "X-RequestDigest: " + CkXml::ckGetChildContent(xml,"d:FormDigestValue")


    CkFileAccess::ckDispose(fac)
    CkXml::ckDispose(xml)
    CkDateTime::ckDispose(dtExpire)
    CkDateTime::ckDispose(dtNow)
    CkHttp::ckDispose(http)
    CkHttpResponse::ckDispose(resp)
    CkXml::ckDispose(xml2)


    ProcedureReturn
EndProcedure