The following article is now DEPRECATED. Although this method will still work within the ServiceNow product, there is now a GUI based utility in ServiceNow called “REST Message”. This utility will allow you to set up a REST web service call using an interface rather than a pure coding play.
I recently came across a customer that needed to integrate with a third-party application that could only speak REST rather than the more standard and traditional SOAP web service method. Don’t get me wrong, REST has it’s followers and it’s strengths. However, because it is so simple, it often gets looked down upon as something that is unworthy of enterprise level integration.
Service-now.com doesn’t have a published API or library for dealing with REST, but it has all of the capabilities inside of it to send RESTful requests and interpret the corresponding responses.
Of course, REST allows for GET, POST, PUT, and DELETE methods. This blog entry is really just focusing on the GET method, but once you get that down, you are able to easily move into the other methods.
Since REST is really just HTTP requests, we know that Service-now can make these types of requests through Java Packages. To make things a little more concise and readable, I put my Package calls into some handy variables as seen below:
1 2 3 4 5 | var httpclient = Packages.org.apache.commons.httpclient; var HttpClient = httpclient.HttpClient; var UsernamePasswordCredentials = httpclient.UsernamePasswordCredentials; var AuthScope = httpclient.auth.AuthScope; var GetMethod = httpclient.methods.GetMethod; |
My demo script is going to be run within one Service-now.com demo instance to call another Service-now demo instance. This shows how Service-now can both request RESTfully and receive REST requests (though the focus for this article is to send the request).
Since Service-now instances are authenticated via Basic Authentication, we are going to use Basic Authentication in our code to connect to the main demo instance, “https://demo.service-now.com”.
1 2 3 4 | var client = new HttpClient(); var authScope = new AuthScope("demo.service-now.com", 443, null) var credentials = new UsernamePasswordCredentials("admin", "admin") client.getState().setCredentials( authScope, credentials); |
Now, really, a GET method in HTTP requests (and REST requests) contains all of the needed parameters in the URL string on the request. This differs from a POST method — so this is a place where you would diverge a bit if doing a POST method rather than a GET.
For our GET request, we are going to look for all Incidents with an Incident State of 1 and a Priority of 1. We also will be asking Service-now to return that in XML format. Here are the pieces of our URL for this GET request:
URL: https://demo.service-now.com/incident_list.do
Encoded Query Parameter: sysparm_query=incident_state%3D1%5Epriority%3D1
Format Processor Parameter: XML
The resultant javascript call looks like this:
1 | var get = new GetMethod( "https://demo.service-now.com/incident_list.do?sysparm_query=incident_state%3D1%5Epriority%3D1&XML"); |
Next, we want to tell the GET method to automatically authenticate us using the Basic Auth information provided above.
1 | get.setDoAuthentication( true ); |
Now that our GET method and HTTP Client objects are set up, we execute the method using the following code:
1 | var status = client.executeMethod( get ); |
The “status” variable will receive the HTTP status code back. If we want to see the resulting data returned, we use the call: getResponseBodyAsString(). Since we are having SNC return an XML document back to us, I decided to escape the “<” characters to help view the result better in the browser. This is really not necessary when handling the data programmatically, but it was helpful for demo reasons.
1 2 | var result = (status + "\n" + get.getResponseBodyAsString()).replace(/</g, "&"+"lt;"); gs.print(result); |
When you are done, don’t forget to release the GET connection:
1 | get.releaseConnection(); |
Since the response in this exercise was in the XML format, we could easily send that to the XMLDocument() class so that we could run XPATH queries on the response and handle the data accordingly. If we requested a JSON response, then we could easily convert the JSON response to javascript objects and continue on to do some interesting things.
Another thing you could do is verify the response by reviewing the Response Headers. Your code would go just after the execute method and would look something like this:
1 2 3 4 | var headers = get.getResponseHeaders(); for (var i=0;i<headers.length;i++){ gs.print(headers[i].getName() + ":"+headers[i].getValue()); } |
Also, if you needed to use the POST method, your code would change slightly at the point where you originally declared the “Get” method, you would define a “Post” method instead. You would also need to individually add the various parameters rather than include them in your URL string. Here is a sample snippet:
1 2 3 4 | var PostMethod = httpclient.methods.PostMethod; var post = new PostMethod("https://demo.service-now.com/incident_list.do"); post.addParameter("sysparm_query", "incident_state%3D1%5Epriority%3D1"); post.addParameter("xml", ""); |
If you were to need Service-now.com to be a full-fledged REST provider (meaning 3rd party apps are calling into SNC through REST), you have the tools at your disposal to easily provide query responses. However, if it were required for REST calls to be made that enacted changes in the SN instance, you would probably create a Processor to handle those REST calls and react accordingly. That, however, is a discussion for another day!
Below you will find the entire code that was used for this example. Feel free to plug it into your instance and run it against SNC’s demo instance to see what kind of response you get.
The Code:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 | var httpclient = Packages.org.apache.commons.httpclient; var HttpClient = httpclient.HttpClient; var UsernamePasswordCredentials = httpclient.UsernamePasswordCredentials; var AuthScope = httpclient.auth.AuthScope; var GetMethod = httpclient.methods.GetMethod; var client = new HttpClient(); var authScope = new AuthScope("demo.service-now.com", 443, null) var credentials = new UsernamePasswordCredentials("admin", "admin") client.getState().setCredentials( authScope, credentials); // create a GET method that reads a file over HTTPS, // we know that this file requires basic // authentication var get = new GetMethod( "https://demo.service-now.com/incident_list.do?sysparm_query=incident_state%3D1%5Epriority%3D1&XML"); // Tell the GET method to automatically handle authentication. The // method will use any appropriate credentials to handle basic // authentication requests. Setting this value to false will cause // any request for authentication to return with a status of 401. // It will then be up to the client to handle the authentication. get.setDoAuthentication( true ); // execute var status = client.executeMethod( get ); //Print out the GET Headers var headers = get.getResponseHeaders(); for (var i=0;i<headers.length;i++){ gs.print(headers[i].getName() + ":"+headers[i].getValue()); } // print the status and response var result = (status + "\n" + get.getResponseBodyAsString()).replace(/</g, "&"+"lt;"); gs.print(result); // release any connection resources used by the method get.releaseConnection(); |
John,
This was great, exactly what I was looking for. Thanks for taking the time to write this up!
May I know where to put those code in SNC? Thank you so much!
Hi John,
I use your code to extract data from my service-now instance. if record contains special characters then it was not extracting in proper format.
e.g. Jesús was getting extracted as Jesús
To resolve this issue I modified your code little bit. instead of using getResponseBodyAsString used getResponseBodyAsStream. code change is as follows.
var inStream = new Packages.java.io.InputStreamReader(get.getResponseBodyAsStream(), “UTF-8”);
var sw = new Packages.java.io.StringWriter();
var x;
while((x = inStream.read()) != -1){
sw.write(x);
}
inStream.close();
var responseAsString = sw.toString();
gs.print(responseAsString);
@Amit,
I like it! Streaming is much better in most cases anyway. Thanks for sharing!
Hi John,
Would it be possible to make HTTP GET requests like this for RESTful web services through a MIB Server?
Thanks,
Seb
@Seb,
It is a little different to go through a MID Server on this. You would have to use a JavascriptProbe using a “MID Server Script Include”. There is not a ton of documentation on this feature, but it is powerful.
Do a search on my blog for “MID Server Script Include” to see any blogs where I use them.
https://john-james-andersen.com/?x=0&y=0&s=%22mid+server+script+include%22
I intend to create another blog just around how to use MID Server Script Includes.
Another “Todo” item for me. 🙂
-John
Hey John,
Any specific reason for using port 443 ?
Thanks!
@adiddigi: Yes, in this case, because we were connecting to a ServiceNow instance which uses port 443 for HTTPS. Other services may be on port 80, or any port. Really just depends on the web service.
John,
I’ve plugged this script into a Run Script activity in a Workflow, which I then call from a Service Catalog Item.
I get a fault with an error message of:
“post” is not defined.
I’m assuming that the complaint is with these lines:
//Print out the GET Headers
var headers = post.getResponseHeaders();
Is there an include or declaration that is needed somewhere?
@Chris,
One of the examples had a typo. It used the “post” variable from an example higher up in the blog entry, but it should have been a “get” variable. I have updated the script.
Good catch. Thanks for bringing it to my attention.
Hi John,
How would you access the X.509 key store within Serivice-Now to attach an X.509 certificate to the http request using your example?
Hi John,
Can you let us know if we can attach a certificate to the https – get message when using REST web-service.
@Vamsi, There is no WS-Security protocol with REST that I am aware of…indeed none with ServiceNow. SOAP requests do have that capability.
ServiceNow does have the capability of enabling Mutual Authentication for outbound HTTP requests. This may be something that would benefit you. Check out the documentation at: http://wiki.servicenow.com/index.php?title=Embedded:Web_Service_Security#Mutual_Authentication
hi,
In the ServiceNow Berlin version, there is a built in support of REST messages.
The problem is that its not so documented – so its difficult to understand how to configure the endpoint, credentials and mid server using script include.
Yonatan,
You are correct. At the time of this article, ServiceNow did not offer a GUI REST Configuration tool. I’ll have to mark this article as deprecated.
This is a great post and as you mentioned ServiceNow has a GUI for this.
However, frustrating that SN prohibits package imports. This script in your example on calgary doesn’t even function.