Requirements    
Prerequisite knowledge Required products
User level
ColdFusion 
Adobe ColdFusion Enterprise Edition (2016 release) (Download trial) All
  Adobe ColdFusion Builder (2016 release) (Download trial)  

 

In ColdFusion 10, we have added support for creating and publishing REST services. You can now make the ColdFusion components available as REST services so that various clients can access them. REST stands for Representational State Transfer. It is an architectural style based on web standards and HTTP protocol. The idea here is to use HTTP protocol instead of complex mechanisms such as CORBA, RPC, or SOAP to connect between the machines. In fact, you can imagine the World Wide Web, which is based on HTTP, as a REST-based architecture.
 

REST architectural principles:

 
A REST-based application follows some architectural principles:

 

    1. Resource identification: In REST-based architecture everything is a resource. You must identify each of these resources with an URI. In ColdFusion, you can make the functions you define in a CFC available as REST resources and you can access them using an URI.
    2. Uniform and constrained interface: Every resource in a RESTful application should support HTTP common operations i.e. the resources should be able to handle HTTP protocol methods –GET,PUT,POST and DELETE. In ColdFusion, you can access the resources (functions) via HTTP; each of these resources supports HTTP verbs. Depending on the verb specified in the request, the corresponding resource is invoked.
    3. Representation oriented: REST allows resources to have different representation – plain, HTML, XML, JSON, and so forth. A client can request a specific representation via the HTTP protocol. HTTP provides a simple content-type negotiation protocol between the client and the server. For example, an AJAX application may need data in JSON format, where as a Java application may need it in XML format.
    4. Stateless communication: In REST, the server doesn't store the client session data. However, many techniques exist to exchange state information such as URI rewriting, cookies, and hidden form fields. Also, for encryption, you can use REST on top of HTTPS.

 

Getting Started with REST services in ColdFusion 10

In ColdFusion 10, we have added new attributes to help you create a REST service to the following ColdFusion tags: cfcomponent,cffunction and cfargument. Once you create a component and register it with the Administrator, you can access it via a HTTP request.
 
Creating a RESTful web service
cfcomponent:
As mentioned above, the methods in a CFC are the resources and you can invoke them using an HTTP request. To mark a component as a REST service, you use the new attributes rest and restpath. The attribute rest is a boolean attribute; when you set it as true, it specifies the component as a REST service. This is an optional attribute if you specify the restpath attribute in the cfcomponent tag. If you do not specify the restpath attribute, ColdFusion uses the path to the CFC in the URI to invoke the service.
 
cffunction:
 
We have added new attributes to the cffunction tag to handle REST requests. The new attributes are: httpmethod, produces, consumes and restpath.

 

Use the httpmethod attribute to specify the type of request the function handles. The possible values are GET, POST, PUT, DELETE, HEAD and OPTIONS. For example, let's say there are two methods defined in the CFC: method1 and method2, each of which with their httpmethod attribute set to GET and POST respectively. ColdFusion would invoke method1 if it received a GET request; ColdFusion would invoke method2 if it received a POST request sent on the same URI matching the restpath attribute value.
 
The produces attribute specifies the format or the content-type of the response. It can be any valid mime-type such as text/plain, text/html, application/json, application/xml, and so forth. You can send complex data types in ColdFusion such as struct, array, query and objects of a component to the client in either JSON or XML format.

 

The consumes attribute specifies content-type of request body, which the method processes. If a method specifies application/x-www-form-urlencoded in the consumes attribute, ColdFusion will process only the request with a request body with content-type application/x-www-form-urlencoded.
 
You must specify the restpath attribute at the function level and also in the URI when accessing the REST service. You can also specify this attribute to specify a template value, for example {customerId} . Here, you can specify the data that you need to pass to the method in the URI itself (see PathParams below), which copies the same to methods argument.
 
cfargument:
 
We added attributes to the cfargument tag as well: restargsource and restargname
. You can pass data in many ways to a REST service. The arguments of a method must know from where to extract the values from the request. Specifying the restargsource attribute in the cfargument tag extracts the data from the request; the attribute can contain one of the following values: PATH, QUERY, FORM, MATRI, HEADER, and COOKIE (see the section, Passing data to a REST service, below).
 
This is shown in the following code example:
 
<!--- component with attributes rest and restpath ---> <cfcomponent rest="true" restpath="/crudService"> <!--- handle GET request (httpmethod), take argument in restpath(restpath={customerID}), return query data in json format(produces=text/json) ---> <cffunction name="getHandlerJSON" access="remote" httpmethod="GET" restpath="{customerID}" returntype="query" produces="application/json"> <cfargument name="customerID" required="true" restargsource="Path" type="numeric"/> <cfset myQuery = queryNew("id,name", "Integer,varchar", [[1, "Sagar"], [2, "Ganatra"]])> <cfquery dbtype="query" name="resultQuery"> select * from myQuery where id = #arguments.customerID# </cfquery> <cfreturn resultQuery> </cffunction> </cfcomponent>
 
The component above contains the restpath attribute specified at both component (crudService) and at the function ({customerID}) level. The component includes a function getHandlerJSON to handle a GET request, as specified in the httpmethod
attribute. The component specifies the value {customerID} at the function level, to indicate that the URI will contain a value that is then copied to the argument customerID. Notice that the name of the argument is same as the one specified in curly braces in restpath.

 

The function returns query data (returnType) and then serializes the data to JSON format (produces). ColdFusion serializes all complex types to either JSON or XML format (see the section, Returning Complex types from a REST Service).
 
Registering a REST service:
Once you create a REST service you must register in the ColdFusion Administrator. You can register the directory containing the REST service in one of the ways mentioned below:
 
  • ColdFusion Administrator console
  • ColdFusion Admin API
  • By calling the method restInitApplication
When registering a directory containing REST-enabled CFCs, you must specify a service mapping identifying the set of CFCs in the directory. You can specify the service mapping explicitly to identify the CFCs or the name used in the Application in the Application.cfc file (see the section on Application.cfc settings for more details).
 
Registering a REST application using ColdFusion Administrator:
You can use the Administrator to register a directory containing REST-enabled CFCs in the REST Services section under Data & Services in ColdFusion Administrator console.
 
Figure 1. Registering a directory containing REST-enabled services
Figure 1. Registering a directory containing REST-enabled services
 
Specify the Root path and Service Mapping for the directory containing a set of REST enabled CFCs. Service mapping is an optional field, if there is an Application.cfc file present in the directory. In this case, use the the Application name to identify the REST services in the directory. Use service mapping or the name of the Application in the URI to access the REST request.
 
It is also possible to mark the REST application as a default application by selecting the option Set as default application. In doing so, you omit the need to specify the service mapping or the application name in the URI. However, you can mark only one application on the server as a default application.
 
By selecting the Add Service button, you register the directory in the ColdFusion Administrator. If there are any compilation errors in any of the CFCs, ColdFusion displays an error message and the registration of the REST application fails (in other words, if this.restsettings.skipCFCWithError=false in the Application.cfc file).
 
Registering a REST application using ColdFusion Admin API:
You can use functions defined in CFIDE.adminapi.extensions CFC to manage a REST application. These functions are:
  1. registerRESTService(rootPath, [service mapping]): This function registers the REST application. TherootPathspecifies the directory containing REST-enabled CFCs. Optionally theservice mapping for the REST application can also be specified.
  2. getRESTServices() : This function returns an array of REST services registered with the ColdFusion Administrator.
  3. deleteRESTService(rootPath) : This function deletes the specified REST application registered with ColdFusion Administrator.
  4. refreshRESTService(rootPath): If you make any changes to the REST-enabled CFCs, you can refresh the registered application by calling this function.
Registering a REST application using the restInitApplication method:
The above methods described how you can register a REST application in ColdFusion Administrator using the Admin API and Administrator console. Both of these require Administrator privileges to register an application. You can also register a REST application by calling the method restInitApplication and it doesn't require Administrator privileges. The syntax is as follows: 
 
restInitApplication(rootPath,[serviceMapping])
 
If you have already registered the application with the Administrator, a call to restInitApplication refreshes the REST service.
 
It is also possible to delete the REST service by calling the restDeleteApplication function. The syntax is as follows:
 
restDeleteApplication(rootPath)
 
Application.cfc settings:
There are a couple of variables introduced in Application.cfc that are REST specific. These are this.restsettings.cfclocation and this.restsettings.skipCFCWithError. If you have a list of directories containing REST-enabled CFCs, you can specify the same in the variable this.restsettings.cfclocation. At the time of registration, ColdFusion will scan the specified directories and its subdirectories for REST-enabled CFCs before deploying them. If any CFC contains compilation errors, ColdFusion throws an error and registration fails. To tackle this situation, we have provided another variable this.restsettings.skipCFCWithError. When you set this variable to true, ColdFusion skips the CFCs with compilation errors. ColdFusion deploys only those without any compilation errors.
 
For example:
 
<cfcomponent> <cfset this.name="restappcfc"> <cfset this.restsettings.cfclocation = "./cfcdir1,./cfcdir2"> <cfset this.restsettings.skipcfcwitherror = true> </cfcomponent>

 

Accessing a REST Service through HTTP
You can access a RESTful web service by using HTTP or HTTPS. The URL to access a REST service in ColdFusion uses the following format:
 
http://servername:port/context_path_j2ee/
rest/rest_application_name_or_serverMapping/restpath_or_componentPath/additionalrestPaths/params_ifany/

 

The following list explains the properties you can set in your URL:
  • context_path_j2ee is applicable only if you have installed ColdFusion as J2EE on any of the Application servers. In that case the context path is cfusion (by default). However, this can be omitted on a ColdFusion Server Stand-alone installation.
  • rest in the URL specifies that the request is for a REST service. ColdFusion has a servlet mapping for the same and would direct the request to the servlet that handles REST service. If there is a directory in the server webroot with the same name, you must update the servlet mapping in web.xml file inside wwwroot\WEB-INF directory. Also, you must update the same mapping in the uriworkermap.properties file located under the config\wsconfig\1 of the server directory.
  • As mentioned earlier, when registering a REST service, ColdFusion uses the application name you specify in Application.cfc file in the URL to access the REST service. If there is no Application.cfc, ColdFusion uses the specified service mapping. Also, you can omit this by marking the application as a default application.
  • To access a service specified in the component you must specify the restpath for the component. However, if you have not specified the restpath in the cfcomponent tag, ColdFusion will use the path to the CFC in the URL. Also, if you define the restpath attribute at the function level, you must specify the same in the URI.
In this example, you can access the function getHandlerJSON using the cfhttp tag, as follows:
 
<cfhttp url="http://localhost:8500/rest/restapp/crudService/1" result="restResult" method="GET"/>
 
Here restapp is the application name defined in the Application.cfc file. The [crudService] in the path is the restpath specified in the cfcomponent tag and the number [1] in the path is the argument you are passing to the REST service.
 
Passing data to a REST service
There are various ways in that you can pass data to a REST service. The cfargument tag has a new attribute restargsource, which has the following values: path,query,matrix,form,cookie and header.
 
Path parameters:
Here you can pass arguments to a REST service by specifying the same in the URL path. The URL may look like the following:
 
http://localhost:8500/rest/restapp/customerService/CF/Zeus
 
Here, the URL passes two values,CF and Zeus as an argument to the REST service. The function defined as a REST service in the CFC will have two arguments; the function's restpath would have two parameters:
 
restPath = {product}/{codeName}
 
The function arguments are the same as the one specified in the restPath, in other words, the two arguments are product and codeName. ColdFusion copies the values CF and Zeus to these parameters.

 

Query parameters:
Query parameters are the most common way to pass data to a REST service by specifying the parameters in URL after question mark (?). The URL would look like the following:
 
http://localhost:8500/rest/restapp/productService?productName=ColdFusion&productCodeName=Zeus
Unlike path parameters, you specify the name of the argument in the URL, separating the arguments with an ampersand (&). You do not need to define the restpath attribute in the function to handle this service since you pass the argument as a query parameter in the URL. Likewise, change the restargsource value to query.
 
Matrix parameters:
Similar to query parameters, in a matrix parameter, you must pass the arguments to a REST service by specifying same information in the URL, separating arguments with a semicolon(;). Therefore the URL would look like the following:
 
http://localhost:8500/rest/restapp/productService;productName=ColdFusion;productCodeName=Zeus
Observe that there is no question mark (?) used in the URL. Instead, matrix parameters use a semicolon. Here the function providing the REST service must have the argument restargsource set to matrix.
 
Cookie and Header parameters:
In the case of Cookie and Header parameters, ColdFusion sends the data through HTTP request. While sending a REST service request, you can include your information by specifying it in the chttpparam tag. In cases where the variable name contains a hyphen (-), let's say the request header may contain variable fname-lname. On the server side, the argument name cannot be fname-lname because it is not a valid identifier. To handle this, there is an attribute restargname. You can specify the argument name as anything, for instance, fname_lname and restargname as fname-lname. The value that you pass to fname-lname maps to fname_lname.
 
Form parameters:
In many scenarios, you must process data that you enter in the form in a REST service. You can use this data to make a new entry (POST) or update an existing record (PUT) in the database. If you use form fields, set restargsource to form. This extracts the data present in the request and makes it available for further processing. Also, you must set the content-type header to application/x-www-form-urlencoded when sending data as form fields.
 

Returning complex data from a REST service

 

There are various complex data types in ColdFusion:Array, Struct, Query. When a REST service in ColdFusion returns one of these complex types, you must serialize it to either JSON or XML format.
 
JSON format:
Consider the following REST service:
 
<cfcomponent rest="true" restpath="/jsonSerialization" produces="application/JSON"> <cffunction name="structSerialization" access="remote" httpmethod="GET" returntype="Struct" restpath="serializeStruct"> <cfset myStruct = StructNew()> <cfset myStruct.productName = "ColdFusion"> <cfset myStruct.productVersion = 10> <cfset myStruct.codeName = "Zeus"> <cfreturn myStruct> </cffunction> <cffunction name="querySerialization" access="remote" httpmethod="GET" returntype="Query" restpath="serializeQuery"> <cfset myQuery = queryNew("productName,version,codeName", "Varchar,Integer,Varchar", [["ColdFusion", 9, "Centaur"], ["ColdFusion", 10, "Zeus"]])> <cfreturn myQuery> </cffunction> <cffunction name="arraySerialization" access="remote" httpmethod="GET" returntype="Array" restpath="serializeArray"> <cfset myArray = ArrayNew(2)> <cfset ArrayAppend(myArray, ["ColdFusion", 9, "Centaur"])> <cfset ArrayAppend(myArray, ["ColdFusion", 10, "Zeus"])> <cfreturn myArray> </cffunction> <cffunction name="cfcSerialization" access="remote" httpmethod="GET" returntype="TestComponent" restpath="serializeCFC"> <cfset obj = new TestComponent()> <cfset obj.prop1 = "CF"> <cfset obj.prop2 = "Zeus"> <cfreturn obj> </cffunction> </cfcomponent>
 
As you can see, the produces attribute is specified at the component level and not at the function level. This indicates that all the services in this component produce or return data in the specified format (application/json format here). However, if you specify the produces attribute at both the component and function level, the produces attribute specified at the function level takes precedence.
You can serialize the complex types, such as Array, Struct, Query, and CFC to a JSON format. You can invoke the above REST service using CFHTTP with the URI:
 
http://localhost:8500/rest/complexTypes/jsonSerialization/serializeQuery.json
 
(to obtain query data)
 
The deserialized output is as shown below:
 
Figure 2. The above struct contains the query data as well as the columns.
Figure 2. The above struct contains the query data as well as the columns.
 

 

XML format:
Similar to the JSON format, if the produces attribute value changes to application/XML, ColdFusion returns the complex data from a REST service serialized in XML format. See the output produced by invoking a REST service that returns CFC data serialized in XML format:
 
<COMPONENT ID="1" NAME="restapp.ComplexTypes.TestComponent"> <PROPERTY NAME="PROP1" TYPE="STRING">CF</PROPERTY> <PROPERTY NAME="PROP2" TYPE="STRING">Zeus</PROPERTY> </COMPONENT>

 

Similarly, you can serialize other complex data types to JSON or XML format.

 

Using HTTP content negotiation to invoke a REST service

 
The HTTP protocol provides a content negotiation mechanism, which allows you to serve different formats of the document using the same URI. For example, a JavaScript application can request the content in JSON format. Likewise, an external system, such as a Java client, can request the same content in XML format. Here, the clients must specify the content format in the Accept attribute of the HTTP request. The REST service can specify which format ColdFusion will return the data in the produces attribute.

 

In ColdFusion, if you want to retrieve content in either JSON or XML format, you can append the content-type to the end of the URI. In other words, the URI would be as follows:

 

http://localhost:8500/rest/contentNeg/customerService/1.json

 

or

 

http://localhost:8500/rest/contentNeg/customerService/1.xml

 

Observe that you specify the content-type at the end of the URI (json\xml). ColdFusion parses the URI and invokes the corresponding REST service. The code below lists the two services that produce the data in JSON and XML format:

 

<cfcomponent rest="true" restpath="/customerService"> <cffunction name="pathHandlerJSON" access="remote" returntype="query" httpmethod="GET" produces="application/json" restpath="{customerID}"> <cfargument name="customerID" required="true" type="string" restargsource="path"/> <cfset resultQuery = retrieveCustomerRecord(arguments.customerID)> <cfreturn resultQuery> </cffunction> <cffunction name="pathHandlerXML" access="remote" returntype="query" httpmethod="GET" produces="application/xml" restpath="{customerID}"> <cfargument name="customerID" required="true" type="string" restargsource="path"/> <cfset resultQuery = retrieveCustomerRecord(arguments.customerID)> <cfreturn resultQuery> </cffunction> <cffunction name="retrieveCustomerRecord" returntype="query"> <cfargument name="customerID" type="numeric" required="true"/> <cfset myQuery = queryNew("id,name", "Integer,varchar", [[1, "Sagar"], [2, "Ganatra"]])> <cfquery dbtype="query" name="resultQuery"> select * from myQuery where id = #arguments.customerID# </cfquery> <cfreturn resultQuery> </cffunction> </cfcomponent>

The functions pathHandlerJSON and pathHandlerXML return query data in JSON or XML format. As observed, the only difference between the two functions is that the produces attribute is different. ColdFusion converts complex data types to JSON or XML format and sends the data to the client.
 
As mentioned earlier, the client can specify the desired content-type in the Accept header in the HTTP request. For example, a ColdFusion client can send a CFHTTP request with the Accept header specified in the CFHTTPPARAM tag, as follows:
 
<cfhttp url="http://localhost:8500/rest/contentNeg/customerService/1" method="get" result="resultJSON"> <cfhttpparam type="header" name="Accept" value="application/json"> </cfhttp> <cfdump var="#deserializeJSON(resultJSON.filecontent)#">
 
When making request through the cfhttp tag, you can make the expected content-type any format (text/html, text/plain, and so forth), by specifying it in the cfhttpparam tag. Yet, you can specify JSON and XML only through the URI.
 
Figure 3. de-serialized data from an HTTP result.
Figure 3. de-serialized data from an HTTP result.
 

 

Using a sub-resource locator

 
In REST, a sub-resource locator refers to a resource that returns an object that handles the HTTP request. In this example, I treat the returned object as a resource class that I use to handle the request or forward it by returning a resource instance. The resource that handles the request must have httpmethod attribute set to a valid HTTP verb.
 
For example:
Consider the ResourceOne component, which I have specified as a REST service by providing the restpath attribute in the cfcomponent tag. Also, this component defines a method, function1.
 
Code for ResourceOne.cfc:
<cfcomponent rest="true" restpath="component1"> <cffunction name="function1" access="remote" returntype="ResourceTwo" restpath="resource1"> <cfset obj = createObject("component", "ResourceTwo")> <cfreturn obj> </cffunction> </cfcomponent>

 

In the code snippet, the function defines the restpath attribute but doesn't define the httpmethod attribute. Also, it returns an instance of the object, ResourceTwo.This would forward the request to the ResourceTwo component and if this component defines functions that can handle HTTP requests, one of the functions will serve the request:
 
Code for ResourceTwo.cfc:<cfcomponent>
 
<cffunction name="function1" access="remote" returntype="string" httpmethod="GET"> <cfreturn "From ResourceTwo, handling GET request"> </cffunction> <cffunction name="function2" access="remote" returntype="string" httpmethod="POST"> <cfreturn "From ResourceTwo, handling POST request"> </cffunction> </cfcomponent>

 

The CFC defines two functions, function1 and function2, which handle GET and POST requests respectively.
 
The HTTP request to access the component is as follows:
 
http://localhost:8500/rest/subresourceloc/component1/resource1
 
This example URI assumes that the value for subresourceloc is the name of the application.

 

Custom responses from a REST service

 

ColdFusion provides default HTTP success and failure responses to the client; you also have the ability to create custom responses.
 
Custom responses using the restSetResponse function:
We have added a new function restSetResponse to list of functions. The syntax is as follows:
 
restSetResponse(response);
 
The response object is a data type of struct, and can contain the following keys: status,content, and headers. The status key contains the status code of the response. The content key contains the response body to be sent to the client.
 
For example, the following code is for the CustomerService.cfc file:
 
<cfcomponent restpath="customerService"> <cffunction name="function1" access="remote" httpmethod="GET" returntype="void"> <cfset customResponse = structNew()> <cfset customResponse.status = "201"> <cfset customResponse.content = "From customresponse"> <cfset customResponse.headers = structNew()> <cfset customResponse.headers.location = "http://www.adobe.com"> <cfset restSetResponse(customresponse)> </cffunction> </cfcomponent>

 

You must set the returntype attribute of the function to void when a custom response is returned using the function restSetResponse. The code snippet sets the status code, response body (content) and header information in a response object (struct) and sends it to the client using the restSetResponse function.
 
A custom response using the cfthrow tag:
There are many cases where an error occurs on the server side while executing the REST service. To notify the client of the error, you can use the cfthrow tag to send a custom response.
 
For example, seeCustomerService1.CFC:
 
<cfcomponent restpath="customerService1"> <cffunction name="function1" access="remote" httpmethod="GET" returntype="string"> <cfthrow errorcode="655" detail="Exception occurred while executing the request" message="Database exception" type="Application"> </cffunction> </cfcomponent>

 

You can use the cfthrow tag to specify the status code by providing a value to the errorcode attribute.You can include the exception detail, message, and type in the response body when returning the data to the client.
 

Where to go from here

 

For more information on building RESTful services with ColdFusion, refer to the Adobe online help documentation. Also, visit my blog, Sagar Ganatra's Blog: A blog on ColdFusion and other web technologies.