Publishing web services

To publish web services for consumption by remote applications, you create the web service using ColdFusion components. For more information on components, see Chapter 11, "Building and Using ColdFusion Components".

Creating components for web services

ColdFusion components encapsulate application functionality and provide a standard interface for client access to that functionality. A component typically contains one or more functions defined by the cffunction tag.

For example, the following component contains a single function:

<cfcomponent>
  <cffunction name="echoString" returnType="string" output="no">
    <cfargument name="input" type="string">
    <cfreturn #arguments.input#>
  </cffunction>
</cfcomponent>

The function, named echoString, echoes back any string passed to it. To publish the function as a web service, you must modify the function definition to add the access attribute, as the following example shows:

<cffunction name="echoString" returnType="string" output="no" access="remote" >

By defining the function as remote, ColdFusion includes the function in the WSDL file. Only those functions marked as remote are accessible as a web service.

The following list defines the requirements for how to create web services for publication:

  1. The value of the access attribute of the cffunction tag must be remote.
  2. The cffunction tag must include the returnType attribute to specify a return type.

    If the function does not return anything, set its returnType attribute to void.

  3. The output attribute of the cffunction tag must be set to No because ColdFusion converts all output to XML to return it to the consumer.
  4. The attribute setting required="false" for the cfargument tag is ignored. ColdFusion considers all parameters as required.

Specifying data types of function arguments and return values

The cffunction tag lets you define a single return value and one or more input parameters passed to a function. As part of the function definition, you include the data type of the return value and input parameters.

The following example shows a component that defines a function with a return value of type string, one input parameter of type string, and one input parameter of type numeric:

<cfcomponent>
  <cffunction name="trimString" returnType="string" output="no">
    <cfargument name="inString" type="string">
    <cfargument name="trimLength" type="numeric">
  </cffunction>
</cfcomponent>

As part of publishing the component for access as a web service, ColdFusion generates the WSDL file that defines the component where the WSDL file includes definitions for how ColdFusion data types map to WSDL data types. The following table shows this mapping:
ColdFusion data type
WSDL data type
numeric
SOAP-ENC:double
boolean
SOAP-ENC:boolean
string
SOAP-ENC:string
array
SOAP-ENC:Array
binary
xsd:base64Binary
date
xsd:dateTime
guid
SOAP-ENC:string
uuid
SOAP-ENC:string
void (operation returns nothing)

struct
Map
query
QueryBean
any
complex type
component definition
complex type

In most cases, consumers of ColdFusion web services will be able to easily pass data to and return results from component functions by mapping their data types to the WSDL data types shown above.

For ColdFusion structures and queries, clients might have to perform some processing to map their data to the correct type. For more information, see "Publishing web services that use complex data types".

You can also define a data type in one ColdFusion component based on another component definition. For more information on using components to specify a data type, see "Using ColdFusion components to define data types for web services".

Producing WSDL files

ColdFusion automatically creates a WSDL file for any component referenced as a web service. For example, if you have a component named echo.cfc in your web root directory, you can view its corresponding WSDL file by requesting the component as follows:

http://localhost/echo.cfc?wsdl

For example, you define a ColdFusion component as follows:

<cfcomponent>
  <cffunction 
name = "echoString" 
returnType = "string" 
output = "no"
access = "remote">
    <cfargument name = "input" type = "string">
    <cfreturn #arguments.input#>
  </cffunction>
</cfcomponent>

If you register the component in Dreamweaver MX, it appears in the Application panel as the following figure shows:

The echo web service shown in Dreamweaver MX

Requesting the WSDL file returns the following:

<?xml version="1.0" encoding="UTF-8" ?> 
<wsdl:definitions targetNamespace="http://webservices" 
xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/" 
xmlns:xsd="http://www.w3.org/2001/XMLSchema" 
xmlns:wsdlsoap="http://schemas.xmlsoap.org/wsdl/soap/" 
xmlns:intf="http://webservices" 
xmlns:impl="http://webservices-impl" 
xmlns:SOAP-ENC="http://schemas.xmlsoap.org/soap/encoding/" 
xmlns="http://schemas.xmlsoap.org/wsdl/">
  <wsdl:message name="echoStringResponse">
      <wsdl:part name="return" type="SOAP-ENC:string" /> 
  </wsdl:message>
  <wsdl:message name="echoStringRequest">
    <wsdl:part name="input" type="SOAP-ENC:string" /> 
  </wsdl:message>
  <wsdl:portType name="echo">
    <wsdl:operation name="echoString" parameterOrder="in0">
      <wsdl:input message="intf:echoStringRequest" /> 
      <wsdl:output message="intf:echoStringResponse" /> 
    </wsdl:operation>
  </wsdl:portType>
  <wsdl:binding name="echo.cfcSoapBinding" type="intf:echo">
    <wsdlsoap:binding style="rpc" transport="http://schemas.xmlsoap.org/soap/http" /> 
    <wsdl:operation name="echoString">
      <wsdlsoap:operation soapAction="" style="rpc" /> 
      <wsdl:input>
        <wsdlsoap:body use="encoded" encodingStyle="http://schemas.xmlsoap.org/soap/encoding/" namespace="http://webservices" /> 
      </wsdl:input>
      <wsdl:output>
        <wsdlsoap:body use="encoded" encodingStyle="http://schemas.xmlsoap.org/soap/encoding/" namespace="http://webservices" /> 
      </wsdl:output>
    </wsdl:operation>
  </wsdl:binding>
  <wsdl:service name="echo.cfcService">
    <wsdl:port name="echo.cfc" binding="intf:echo.cfcSoapBinding">
      <wsdlsoap:address location="http://SMGILSON02/webservices/echo.cfc" /> 
    </wsdl:port>
  </wsdl:service>
</wsdl:definitions>

To publish a web service:

  1. Create a ColdFusion page with the following content:
    <cfcomponent output="false">
      <cffunction 
          name = "echoString" 
          returnType = "string" 
          output = "no" 
          access = "remote">
        <cfargument name = "input" type = "string">
        <cfreturn #arguments.input#>
      </cffunction>
    </cfcomponent>
    
  2. Save this file as echo.cfc in your web root directory.
  3. Create a ColdFusion page with the following content:
    <cfinvoke webservice ="http://localhost/echo.cfc?wsdl"
      method ="echoString"
      input = "hello" 
      returnVariable="foo">
    
    <cfoutput>#foo#</cfoutput>
    
  4. Save this file as echoclient.cfm in your web root directory.
  5. Request echoclient.cfm in your browser.

    The following string appears in your browser:

    hello
    

You can also invoke the web service using the following code:

<cfscript>
  ws = CreateObject("webservice", "http://localhost/echo.cfc?wsdl");
  wsresults = ws.echoString("hello");
  writeoutput(wsresults);
</cfscript>

Using ColdFusion components to define data types for web services

ColdFusion components let you define both methods and properties of the component. Once defined, you can use components to define data types for web services. The following code defines a component in the file address.cfc:

<cfcomponent>
  <cfproperty name="Number" type="numeric">
  <cfproperty name="Street" type="string">
  <cfproperty name="City" type="string">
  <cfproperty name="State" type="string">
  <cfproperty name="Country" type="string">
</cfcomponent>

This component contains properties that represent a street address. The following code defines a component in the file name.cfc that defines first and last name properties:

<cfcomponent>
  <cfproperty name="Firstname" type="string">
  <cfproperty name="Lastname" type="string">
</cfcomponent>

You can then use address and name to define data types in a ColdFusion component created to publish a web service, as the following example shows:

<cfcomponent>
  <cffunction name="echoName" returnType="name" access="remote">
      <cfargument name="input" type="name">
      <cfreturn #arguments.input#>
  </cffunction>

  <cffunction name="echoAddress" returnType="address" access="remote">
      <cfargument name="input" type="address">
      <cfreturn #arguments.input#>
  </cffunction>
</cfcomponent>

Note:   If the component files are not in a directory under your web root, you must create a ColdFusion mapping to the directory containing them.

If you register the component in Dreamweaver MX, it appears in the Application panel as the following figure shows:

The profile web service shown in Dreamweaver MX

The WSDL file for the web service contains data definitions for the complex types name and address. Each definition consists of the elements that define the type as specified in the ColdFusion component file for that type. For example, shown below is the definition for name:

<complexType name="name"> 
  <all> 
    <element name="Firstname" nillable="true" type="xsd:string" /> 
    <element name="Lastname" nillable="true" type="xsd:string" /> 
  </all> 
</complexType>

Securing your web services

You can restrict access to your published web services to control the users allowed to invoke them. You can use your web server to control access to the directories containing your web services, or you can use ColdFusion security in the same way that you would to control access to any ColdFusion page.

Controlling access to component CFC files

To browse the HTML description of a .cfc file, you request the file by specifying a URL to the file in your browser. By default, ColdFusion secures access to all URLs that directly reference a .cfc file, and prompts you to enter a password upon the request. Use the ColdFusion RDS password to view the file.

To disable security on .cfc file browsing, use the ColdFusion Administrator to disable the RDS password.

For more information, see Chapter 11, "Building and Using ColdFusion Components".

Using your web server to control access

Most web servers, including IIS and Apache, implement directory access protection using the basic HTTP authentication mechanism. When a client attempts to access one of the resources under a protected directory, and has not properly authenticated, the web server automatically sends back an authentication challenge, typically an HTTP Error 401 Access Denied error.

In response, the client's browser opens a login prompt containing a username and password field. When the user submits this information, the browser sends it back to the web server. If authentication passes, the web server allows access to the directory. The browser also caches the authentication data as long as it is open, so subsequent requests automatically include the authentication data.

Web service clients can also pass the username and password information as part of the request. The cfinvoke tag includes the username and password attributes that let you pass login information to a web server using HTTP basic authentication. You can include these attributes when invoking a web service, as the following example shows:

<cfinvoke 
  webservice = "http://some.wsdl"
  returnVariable = "foo"
  ...
  username="aName"
  password="aPassword">
<cfoutput>#foo#</cfoutput>

ColdFusion inserts the username/password string in the authorization request header as a base64 binary encoded string, with a colon separating the username and password. This method of passing the username/password is compatible with the HTTP basic authentication mechanism used by web servers.

The ColdFusion Administrator lets you predefine web services. As part of defining the web service, you can specify the username and password that ColdFusion includes as part of the request to the web service. Therefore, you do not have to encode this information using the cfinvoke tag. For information on defining a web service in the ColdFusion Administrator, see "Configuring web services in the ColdFusion Administrator".

Using ColdFusion to control access

Instead of letting the web server control access to your web services, you can handle the username/password string in your Application.cfm file as part of your own security mechanism. In this case, you use the cflogin tag to retrieve the username/password information from the authorization header, decode the binary string, and extract the username and password, as the following example Application.cfm file shows:

<cfsilent>
<cflogin>
 
  <cfset isAuthorized = false>

  <cfif isDefined("cflogin")
    <!--- verify user name from cflogin.name and password from cflogin.password
using your authentication mechanism --->
    >
    <cfset isAuthorized = true> 
  </cfif>
 
</cflogin>

<cfif not isAuthorized>
  <!--- If the user does not pass a username/password, return a 401 error. 
    The browser then prompts the user for a username/password. --->
  <cfheader statuscode="401">
  <cfheader name="WWW-Authenticate" value="Basic realm=""Test""">
  <cfabort>
</cfif>
</cfsilent>

This example does not show how to perform user verification. For more information on verification, see Chapter 16, "Securing Applications".

Assigning security roles to functions

ColdFusion components offer role-based security. The following example creates a component method that deletes files:

<cfcomponent>
  <cffunction name="deleteFile" access="remote" roles="admin,manager">
    <cfargument name="filepath" required="yes">
    <cffile action="DELETE" file=#arguments.filepath#>
  </cffunction>
</cfcomponent> 

In the example, the cffunction tag includes the roles attribute to specify the user roles allowed to access it. In this example, only users in the role admin and manager can access the function. Notice that multiple roles are delimited by a comma.

Role based security can be used with any ColdFusion component, not just for web services. For more information on roles, see Chapter 16, "Securing Applications".

Using programmatic security

You can implement your own security within the a function to protect resources. For example you can use the ColdFusion function IsUserInRole() to determine if a user is in particular role, as the following example shows:

<cffunction name="foo">
  <cfif IsUserInRole("admin")>
    ... do stuff allowed for admin
  <cfelseif IsUserInRole("user")>
    ... do stuff allowed for user
  <cfelse>
    <cfoutput>unauthorized access</cfoutput>
    <cfabort>
  </cfif>
</cffunction>

Best practices for publishing web services

ColdFusion web services provide a powerful mechanism for publishing and consuming application functionality. However, before you produce web services for publication, you might want to consider the following best practices:

  1. Minimize the use of ColdFusion complex types, such as query and struct, in the web services you create for publication. These types require consumers, especially those consuming the web service using a technology other than ColdFusion, to create special data structures to handle complex types.
  2. Locally test the ColdFusion components implemented for web services before publishing them over the Internet.

Comments