This document explains how to use the REST web services that are part of Exact Synergy Enterprise.
Goals
Subjects
Expand All / Collapse All
1.1 Introduction Exact provides a large set of web services with Exact Synergy Enterprise to retrieve, create and update data in Exact Synergy Enterprise. The web services are offered using two protocols: SOAP REST This document focusses on how to use the REST web service of Exact Synergy Enterprise. It will give an example on how to retrieve and create requests including binary data. This document comes with a demo application (including sources): ESE_JSON_REST_Example 1.2 Architecture REST stands for Representational State Transfer. REST is not a protocol or standard but a design architecture. It is an attempt to offer a more simple way for machine interaction than complex mechanisms like CORBA, RPC and SOAP. REST services do this by using simple HTTP requests to exchange data. Like SOAP web services, REST web services are platform independent and language independent. You can implement your client on for example UNIX or Windows and use any language. All popular languages (like VB.Net, C#, Java, JavaScript, Python, etc.) offer libraries to make the use of REST services easy. Although REST is a lightweight architecture it offers all functionality also available with for example SOAP. REST offers all four CRUD (create, read, update and delete) operation by using HTTP requests (PUT, GET, DELETE). Please note that not all Exact web services implement all four CRUD operations. To exchange the data Exact uses the open data protocol. Exact has implemented two data schema’s that can be used: 1) Atom XML 2) JSON In a .Net environment Atom XML is quite convenient, because .Net offers a good XML library. In other environments JSON might be more convenient. The examples in this document use JSON in VB.Net. Exact Synergy Enterprise REST services are governed by IIS and the service model credentials. The Exact web services use NTLM as authentication mechanism. See below for an example of the HTTP header: GET http://dijk177740-vpc1/252/services/Exact.Entity.REST.svc/Request/?$top=1&$filter=RequestID%20eq%205ea5ee9e-723f-47d8-be91-de5a6000b188%20and%20PrepareDownloadRequestComments%20eq%20true%20and%20PrepareDownloadWorkflowComments%20eq%20true HTTP/1.1 Content-Type: application/json Accept: application/json,text/javascript; charset=utf-8 Host: dijk177740-vpc1 Connection: Keep-Alive 1.3 Reference http://www.odata.org/ http://msdn.microsoft.com/en-us/library/ff478141.aspx http://rest.elkstein.org/ http://www.json.org/ Also the Exact database documentation: 18.127.775 will be useful.
This document focusses on how to use the REST web service of Exact Synergy Enterprise. It will give an example on how to retrieve and create requests including binary data. This document comes with a demo application (including sources): ESE_JSON_REST_Example
Like SOAP web services, REST web services are platform independent and language independent. You can implement your client on for example UNIX or Windows and use any language. All popular languages (like VB.Net, C#, Java, JavaScript, Python, etc.) offer libraries to make the use of REST services easy.
Although REST is a lightweight architecture it offers all functionality also available with for example SOAP. REST offers all four CRUD (create, read, update and delete) operation by using HTTP requests (PUT, GET, DELETE). Please note that not all Exact web services implement all four CRUD operations.
To exchange the data Exact uses the open data protocol. Exact has implemented two data schema’s that can be used:
1) Atom XML 2) JSON
In a .Net environment Atom XML is quite convenient, because .Net offers a good XML library. In other environments JSON might be more convenient. The examples in this document use JSON in VB.Net. Exact Synergy Enterprise REST services are governed by IIS and the service model credentials. The Exact web services use NTLM as authentication mechanism.
See below for an example of the HTTP header:
GET http://dijk177740-vpc1/252/services/Exact.Entity.REST.svc/Request/?$top=1&$filter=RequestID%20eq%205ea5ee9e-723f-47d8-be91-de5a6000b188%20and%20PrepareDownloadRequestComments%20eq%20true%20and%20PrepareDownloadWorkflowComments%20eq%20true HTTP/1.1
Content-Type: application/json
Accept: application/json,text/javascript; charset=utf-8
Host: dijk177740-vpc1
Connection: Keep-Alive
Also the Exact database documentation: 18.127.775 will be useful.
The examples in this document are written in VB.Net. All examples are in a single .Net 4.0 windows form application. In the text the main functions will be discussed. To develop and test the examples I used the following tools:
http://localhost/Synergy/services/Exact.Entity.REST.svc/$metadata
This service returns an XML file with the metadata of all available entities.
4.1 Open data query options $top $filter $select $orderby $top is used to pass the (max) amount of records you want to retrieve. $filter is used to pass the filtering you want to apply on the data. The following logical operations are supported: eq Equal ne Not equal gt Greater than ge Greater than or equal lt Less than le Less than or equal and Logical and $select is used to limit the properties that are retrieved. This is a comma separated list of property names. $orderby is used to define the order of the records that are retrieved. This is a comma separated list and you can make use of asc and desc to change the sort order. 4.2 Retrieve URL Combining the different operations, an URL to retrieve a request can look like: http://localhost/Synergy/services/Exact.Entity.REST.svc/Request/?$top=1&$filter=RequestNumber eq 1611 and PrepareDownloadRequestComments eq true and PrepareDownloadWorkflowComments eq true 4.3 Example The example application uses a help function: Private Function RequestAndResponse(ByVal xml As String, ByVal method As String, ByVal url As String) As String The function RequestAndResponse is used to create the HTTP request, send it to the REST service and retrieve the response message. In this function an HttpWebRequest is created and the following properties are set: Credentials (You have to use NTLM credentials) ContentType (This is the type of the payload, in this case atom+xml) Accept (This is the type of the response, in this case also atom+xml) Method (This is the HTTP method, GET, POST, DELETE) With httpRequest.GetResponse() the HttpWebResponse object is retrieved. The response stream of this object contains the retrieved JSON string (or XML text if you’re using atom+xml). Retrieving a request is done in: Private Sub RetrieveRequest() In the RetrieveRequest you can see an URL is created with an OpenData query. When retrieving you don’t have to provide a JSON string. When the URL is send to RequestAndResponse the response stream contains a JSON string with all property values of the request (Of course you can retrieve multiple requests in one response). In the code a JavaScriptSerializer object is used to retrieve the properties: Dim js As New JavaScriptSerializer Dim jsonObject As Dictionary(Of String, Object) = js.DeserializeObject(sResponse) Dim document = CType(jsonObject("d"), Dictionary(Of String, Object)) For Each r As Dictionary(Of String, Object) In document("results") tbRequestType.Text = CType(r("RequestType"), String) You can also use Fiddler to inspect the RAW HTTP messages.
$top is used to pass the (max) amount of records you want to retrieve. $filter is used to pass the filtering you want to apply on the data. The following logical operations are supported:
$select is used to limit the properties that are retrieved. This is a comma separated list of property names. $orderby is used to define the order of the records that are retrieved. This is a comma separated list and you can make use of asc and desc to change the sort order.
The example application uses a help function:
Private Function RequestAndResponse(ByVal xml As String, ByVal method As String, ByVal url As String) As String
The function RequestAndResponse is used to create the HTTP request, send it to the REST service and retrieve the response message.
In this function an HttpWebRequest is created and the following properties are set:
With httpRequest.GetResponse() the HttpWebResponse object is retrieved. The response stream of this object contains the retrieved JSON string (or XML text if you’re using atom+xml).
Retrieving a request is done in:
Private Sub RetrieveRequest()
In the RetrieveRequest you can see an URL is created with an OpenData query. When retrieving you don’t have to provide a JSON string. When the URL is send to RequestAndResponse the response stream contains a JSON string with all property values of the request (Of course you can retrieve multiple requests in one response).
In the code a JavaScriptSerializer object is used to retrieve the properties:
Dim js As New JavaScriptSerializer Dim jsonObject As Dictionary(Of String, Object) = js.DeserializeObject(sResponse) Dim document = CType(jsonObject("d"), Dictionary(Of String, Object)) For Each r As Dictionary(Of String, Object) In document("results") tbRequestType.Text = CType(r("RequestType"), String)
Dim js As New JavaScriptSerializer
Dim jsonObject As Dictionary(Of String, Object) =
js.DeserializeObject(sResponse)
Dim document = CType(jsonObject("d"), Dictionary(Of String, Object))
For Each r As Dictionary(Of String, Object) In document("results")
tbRequestType.Text = CType(r("RequestType"), String)
You can also use Fiddler to inspect the RAW HTTP messages.
Retrieving binary data is a bit more complex, because of the size of the data. To be able to retrieve a large amount of (binary) data the Binary web service offers the data in multiple chunks, that you need to stitch together in your code.
5.1 Retrieve URL and response The retrieve URL looks something like. http://localhost/Synergy/services/Exact.Entity.REST.svc/Binary/?$top=1&$filter=MessageID%20eq%20guid'df7a5a75-cff4-4253-a581-5202454e9ee7' Note that you must provide a guid of the binary data. This guid was retrieved when retrieving the request data see the properties: MessageIDRequestComment MessageIDWorkflowComment The response looks like: {"d":{"results":[{"__metadata":{"id":"http://localhost/Synergy/services/Exact.entity.REST.svc/Binary(guid'df7a5a75-cff4-4253-a581-5202454e9ee7')","uri":"http://localhost/Synergy/services/Exact.entity.REST.svc/Binary(guid'df7a5a75-cff4-4253-a581-5202454e9ee7')","type":"Exact.Metadata.Entity.Binary"},"MessageID":"df7a5a75-cff4-4253-a581-5202454e9ee7","Sequence":0,"Data":"Pz8/Zw==","Encoded":false,"DataString":""}],"__next":"http://localhost/Synergy/services/Exact.Entity.REST.svc/Binary/?$filter=MessageID%20eq%20guid'df7a5a75-cff4-4253-a581-5202454e9ee7'&$skiptoken=2395L"}} Note that the response message contains a property __next, this property contains the URL you need to send to retrieve the next chunk of data. 5.2 Example Retrieving the binary data is done in the function: Private Function RetrieveBinaryStream(sGuid As String) As MemoryStream In this function all the chunks of binary data of a certain messageID is retrieved and stitched together: sUrl = sUrl & String.Format("Binary/?$top=1&$filter=MessageID eq guid'{0}'", sGuid) While sUrl.Length > 0 sResponse = RequestAndResponse("", "GET", sUrl) Dim jsonObject As Dictionary(Of String, Object) = js.DeserializeObject(sResponse) Dim document = CType(jsonObject("d"), Dictionary(Of String, Object)) If document.ContainsKey("__next") Then sUrl = document("__next") If Not sUrl.Contains("$top=") Then sUrl = String.Format("{0}&$top=1", sUrl) Else sUrl = "" End If For Each r As Dictionary(Of String, Object) In document("results") b = Convert.FromBase64String(CType(r("Data"), String)) s.Write(b, 0, b.Length) Next End While s.Close() Please note that there is a while loop to retrieve all chunks until the response message no longer contains a __next property.
http://localhost/Synergy/services/Exact.Entity.REST.svc/Binary/?$top=1&$filter=MessageID%20eq%20guid'df7a5a75-cff4-4253-a581-5202454e9ee7'
Note that you must provide a guid of the binary data. This guid was retrieved when retrieving the request data see the properties:
The response looks like:
{"d":{"results":[{"__metadata":{"id":"http://localhost/Synergy/services/Exact.entity.REST.svc/Binary(guid'df7a5a75-cff4-4253-a581-5202454e9ee7')","uri":"http://localhost/Synergy/services/Exact.entity.REST.svc/Binary(guid'df7a5a75-cff4-4253-a581-5202454e9ee7')","type":"Exact.Metadata.Entity.Binary"},"MessageID":"df7a5a75-cff4-4253-a581-5202454e9ee7","Sequence":0,"Data":"Pz8/Zw==","Encoded":false,"DataString":""}],"__next":"http://localhost/Synergy/services/Exact.Entity.REST.svc/Binary/?$filter=MessageID%20eq%20guid'df7a5a75-cff4-4253-a581-5202454e9ee7'&$skiptoken=2395L"}}
Note that the response message contains a property __next, this property contains the URL you need to send to retrieve the next chunk of data.
Private Function RetrieveBinaryStream(sGuid As String) As MemoryStream
In this function all the chunks of binary data of a certain messageID is retrieved and stitched together:
sUrl = sUrl & String.Format("Binary/?$top=1&$filter=MessageID eq guid'{0}'", sGuid) While sUrl.Length > 0 sResponse = RequestAndResponse("", "GET", sUrl) Dim jsonObject As Dictionary(Of String, Object) = js.DeserializeObject(sResponse) Dim document = CType(jsonObject("d"), Dictionary(Of String, Object)) If document.ContainsKey("__next") Then sUrl = document("__next") If Not sUrl.Contains("$top=") Then sUrl = String.Format("{0}&$top=1", sUrl) Else sUrl = "" End If For Each r As Dictionary(Of String, Object) In document("results") b = Convert.FromBase64String(CType(r("Data"), String)) s.Write(b, 0, b.Length) Next End While s.Close()
sUrl = sUrl & String.Format("Binary/?$top=1&$filter=MessageID eq guid'{0}'", sGuid)
While sUrl.Length > 0
sResponse = RequestAndResponse("", "GET", sUrl)
Dim jsonObject As Dictionary(Of String, Object) = js.DeserializeObject(sResponse)
If document.ContainsKey("__next") Then sUrl = document("__next") If Not sUrl.Contains("$top=") Then sUrl = String.Format("{0}&$top=1", sUrl) Else sUrl = "" End If
For Each r As Dictionary(Of String, Object) In document("results") b = Convert.FromBase64String(CType(r("Data"), String)) s.Write(b, 0, b.Length) Next End While s.Close()
Please note that there is a while loop to retrieve all chunks until the response message no longer contains a __next property.
6.1 HTTP request For creating data you must use the POST method of the HTTP request and send a JSON string as payload. In this JSON string you provide all properties you want to set on the request. 6.2 Metadata Some of most important properties of a request are: Field Value Example RequestID The GUID of the request 4F017C70-8178-4B2D-A885-C08F46530B30 RequestNumber The number of the request 1611 RequestType The type of the request 0 (=Task) Description The description of the request Test Task MessageIDRequestComment The GUID of the binary message that contains the request comment DF7A5A75-CFF4-4253-A581-5202454E9EE7 MessageIDWorkflowComment The GUID of the binary message that contains the workflow comment DF7A5A75-CFF4-4253-A581-5202454E9EE7 6.3 Example The request is created in: Private Sub CreateRequest() This sub calls the function CreateBinary to upload the request comment and the workflow comment. This function is described in the following chapter. sResponse = RequestAndResponse(json.ToString(), "POST", sUrl) Dim jsonObject As Dictionary(Of String, Object) = js.DeserializeObject(sResponse) Dim document = CType(jsonObject("d"), Dictionary(Of String, Object)) tbRequestID.Text = CType(document("RequestID"), String) tbRequestNumber.Text = CType(document("RequestNumber"), String) In this case the RequestAndResponse method is called with the POST method, also we now provide a JSON string with the URL. This JSON string contains the properties we want to set eg: {"RequestType":0,"Description":"Test task","MessageIDRequestComments":"06eac875-eb0f-4bb4-acd6-aa51de43a897","MessageIDWorkflowComments":"5490bc3d-3c31-498f-9561-f66a3e1db032"} The response contains a key: d that contains all the returned property values that were set by Synergy itself. Again I use a JavaScriptSerializer object to retrieve the request ID and request Number.
Field
Value
Example
RequestID
The GUID of the request
4F017C70-8178-4B2D-A885-C08F46530B30
RequestNumber
The number of the request
1611
RequestType
The type of the request
0 (=Task)
Description
The description of the request
Test Task
MessageIDRequestComment
The GUID of the binary message that contains the request comment
DF7A5A75-CFF4-4253-A581-5202454E9EE7
MessageIDWorkflowComment
The GUID of the binary message that contains the workflow comment
The request is created in:
Private Sub CreateRequest()
This sub calls the function CreateBinary to upload the request comment and the workflow comment. This function is described in the following chapter.
sResponse = RequestAndResponse(json.ToString(), "POST", sUrl) Dim jsonObject As Dictionary(Of String, Object) = js.DeserializeObject(sResponse) Dim document = CType(jsonObject("d"), Dictionary(Of String, Object)) tbRequestID.Text = CType(document("RequestID"), String) tbRequestNumber.Text = CType(document("RequestNumber"), String)
sResponse = RequestAndResponse(json.ToString(), "POST", sUrl)
Dim jsonObject As Dictionary(Of String, Object) = js.DeserializeObject(sResponse) Dim document = CType(jsonObject("d"), Dictionary(Of String, Object)) tbRequestID.Text = CType(document("RequestID"), String) tbRequestNumber.Text = CType(document("RequestNumber"), String)
In this case the RequestAndResponse method is called with the POST method, also we now provide a JSON string with the URL. This JSON string contains the properties we want to set eg:
{"RequestType":0,"Description":"Test task","MessageIDRequestComments":"06eac875-eb0f-4bb4-acd6-aa51de43a897","MessageIDWorkflowComments":"5490bc3d-3c31-498f-9561-f66a3e1db032"}
The response contains a key: d that contains all the returned property values that were set by Synergy itself. Again I use a JavaScriptSerializer object to retrieve the request ID and request Number.
7.1 Metadata Field Value Example MessageID The GUID of the message 4F017C70-8178-4B2D-A885-C08F46530B30 Sequence The sequence number of the chunk 0 (, 1, 2, 3) Encoded If the data is Base64 encoded True DataString The (Base64 encoded) data dGVzdCB3b3JrZmxvdyBjb21tZW50 7.2 Example The binary data is created in the function: Private Overloads Function CreateBinary(s As MemoryStream) As String The function returns the GUID of the message which can be passed to another entity like a request (see previous chapter). I use a while loop to upload small chunks of the data. ms.Position = 0 retval = ms.Read(outByte, 0, buffSize) Do While retval = buffSize json.Clear() json.Append("{") If messageID <> "" Then json.Append(String.Format(JSON_STRING, "MessageID", "")) End If json.Append(String.Format(JSON_STRING, "Sequence", packetNum)) json.Append(String.Format(JSON_STRING, "Encoded", "true")) json.Append(String.Format(JSON_STRING, "DataString", """" & Convert.ToBase64String(outByte) & """")) sResponse = RequestAndResponse(json.ToString(), "POST", sUrl) If messageID = "" Then Dim js As New JavaScriptSerializer Dim jsonObject As Dictionary(Of String, Object) = js.DeserializeObject(sResponse) Dim document = CType(jsonObject("d"), Dictionary(Of String, Object)) messageID = CType(document("MessageID"), String) End If retval = ms.Read(outByte, 0, buffSize) Loop Please make sure that after the while loop you upload the remaining part of the stream (see the full source code for this).
MessageID
The GUID of the message
Sequence
The sequence number of the chunk
0 (, 1, 2, 3)
Encoded
If the data is Base64 encoded
True
DataString
The (Base64 encoded) data
dGVzdCB3b3JrZmxvdyBjb21tZW50
Private Overloads Function CreateBinary(s As MemoryStream) As String
The function returns the GUID of the message which can be passed to another entity like a request (see previous chapter). I use a while loop to upload small chunks of the data.
ms.Position = 0 retval = ms.Read(outByte, 0, buffSize) Do While retval = buffSize json.Clear() json.Append("{") If messageID <> "" Then json.Append(String.Format(JSON_STRING, "MessageID", "")) End If json.Append(String.Format(JSON_STRING, "Sequence", packetNum)) json.Append(String.Format(JSON_STRING, "Encoded", "true")) json.Append(String.Format(JSON_STRING, "DataString", """" & Convert.ToBase64String(outByte) & """")) sResponse = RequestAndResponse(json.ToString(), "POST", sUrl) If messageID = "" Then Dim js As New JavaScriptSerializer Dim jsonObject As Dictionary(Of String, Object) = js.DeserializeObject(sResponse) Dim document = CType(jsonObject("d"), Dictionary(Of String, Object)) messageID = CType(document("MessageID"), String) End If retval = ms.Read(outByte, 0, buffSize) Loop
ms.Position = 0 retval = ms.Read(outByte, 0, buffSize) Do While retval = buffSize json.Clear() json.Append("{") If messageID <> "" Then json.Append(String.Format(JSON_STRING, "MessageID", "")) End If json.Append(String.Format(JSON_STRING, "Sequence", packetNum)) json.Append(String.Format(JSON_STRING, "Encoded", "true")) json.Append(String.Format(JSON_STRING, "DataString", """" & Convert.ToBase64String(outByte) & """"))
If messageID = "" Then Dim js As New JavaScriptSerializer
messageID = CType(document("MessageID"), String) End If
retval = ms.Read(outByte, 0, buffSize) Loop
Please make sure that after the while loop you upload the remaining part of the stream (see the full source code for this).
Document Number: 26.572.434
Disclaimer Despite the continued efforts of Exact to ensure that the information in this document is as complete and up-to-date as possible, Exact can not be held accountable for the correctness and/or completeness and/or specific applicability of the published and/or requested information in this document. Exact shall not be liable for any direct, indirect, incidental, special or consequential damages, lost profits or for business interruption arising out of the use of this document. The extraction and use of information from this document remains at all times completely within the user's own risk.