REST Reporting
February 16, 2005
Producing paper reports is a fundamental requirement of many applications. As more systems are exposed as services, REST, XSLT, and the mighty URI can create a reporting approach that has a number of advantages over traditional, database-direct reporting engines. These benefits include:
- Reporting from the business level of an application
- Combining the data from multiple services
- Building a flexible reporting framework that is largely platform and vendor independent
Though REST-based reporting is simple in concept, there are number of issues that can impact the performance of RESTful reporting. This article discusses some of these issues and how they relate to XSLT, REST service design, and toolset selection.
Assembling the Data
Often, the data required to build a report is not available from a single webservice response, and the data must be composited from a number of webservice calls. As a team, REST and XSLT are especially useful tools for accomplishing this. If you are unfamiliar with REST or using XSLT to transform multiple input documents, you may want to first review these XML.com articles:
An example of a REST-based report is this hypothetical status report of open work-orders. Additional details, such as maps and photographs, are included for work-orders that are over-budget.

The construction of this report requires the compilation and transformation of data from a variety of REST resources, each identified by a URI:
- [1] A listing of open work orders from
http://workmgmt/workorders/?status=open
- [2] A detail of one specific work order from
http://workmgmt/workorders/161803
- [3] Basic information and photo about the asset from
http://assetmgmt/summary/23044
- [4] An SVG transform of GML feature data from
http://assetmgmt/maps/23044?radius=2000&layers=parcel,street,utility
The key to making this happen is XSLT's document
function.
A Closer Look at XSLT's Document Function
In the XSLT for the example report, we conditionally obtain detailed work-order information with a request using a relative URI [1].
<xsl:template match="m:workorder"> .. show work order information .. <xsl:if test="m:actualCost > m:budgetCost"> <-- Get work order detail --> <xsl:apply-templates select="document(@workorderID,.)/m:workorder" [1] mode="workorder-detail"/> </xsl:if> </xsl:template>
Using relative URIs with XSLT's document
function requires some care. When expressed as a string, relative URIs are relative
to the
stylesheet's base URI, and when expressed as a node, they are relative to the node's
parent
document. When using relative URIs, you can also use the second argument of the
document
function, which is a node from the document that will be the base of
the relative URI.
As an example, consider the resolution the relative URI 161803
from within
the XSLT document file://d:/workmgmt/xslt/budgetReport.xsl
.
Input Document URI | Document Request | Resource Returned |
---|---|---|
http://workmgmt/workorders/ | document('161803') | file://d:/workmgmt/xslt/161803 |
http://workmgmt/workorders/ | document(string(@workorderID)) | file://d:/workmgmt/xslt/161803 |
http://workmgmt/workorders/ | document(@workorderID) | http://workmgmt/workorders/161803 |
http://workmgmt/workorders | document('161803',.) | http://workmgmt/161803 |
http://workmgmt/workorders/ | document('161803',.) | http://workmgmt/workorders/161803 |
Dynamic XML and Base URIs
If you are performing a server-side XSLT transformation on a dynamically created
XML
source, there may be no base URI from which to derive relative URIs. If there is a
base, it
may be something unfortunate like c:\winnt\system32\
.
One way to deal with this is to explicitly set the base URI of the input XML document. The source of the identity of the base URI depends on your transformation engine; Java engines often use the javax.xml.transform.Source.getSystemId() method, and .Net uses on the XmlReader.BaseURI property.
In some cases (like with many .Net XmlReader implementations), you cannot set the value of the base URI directly. Fortunately, a quick customization the XML source can allow you to explicitly set the base URI. The following example inherits XmlNodeReader, which can be used in .Net to read DOM nodes into an XPathDocument to be used as input for a XSLT transform:
public class ContextXmlNodeReader : XmlNodeReader { private string _baseUri; public ContextXmlNodeReader(XmlNode node, string baseUri) : base(node) { _baseUri = baseUri; } public override string BaseURI { get {return _baseUri;} } }
Handling Local REST Requests with URI Resolvers
The example report in this article shows more detail for some work-orders by making additional requests to the REST service. If a lot of details are shown, generating the report can require a lot of potentially costly HTTP calls. When using XSLT on the server-side, you can avoid having to use an HTTP request for each request by implementing a custom URI resolver implementation to handle local REST GET requests.
URI resolvers are used by many XSLT processors to interpret and return resources
identified in XSLT bits like the <xsl:include>
element or the
document()
function. Custom URI resolvers let you intercept these URI
requests and handle them directly. This is done by overriding methods such as XmlResolver's GetEntity (.NET) or URIResolver's resolve method (java). In the case of our example, all requests for
URIs beginning with "http://workmgmt/workorders
" would be handled directly by
the business logic of the work-order REST service. All other URI requests trigger
the
default behavior of the URI resolver and result in an HTTP GET request.
When your REST service supports server side XSLT transforms, it can sometimes be useful to use a REST handler that can act as a URI resolver to handle local GET requests and as a HTTP handler to handle remote and non-GET requests.

URI Structure
Relative URIs can help you maintain context and simplify requests when creating a
report
detail. While transforming the work-order information from
http://workmgmt/workorder/161803/
, obtaining detailed labor charging
information that is located at http://workmgmt/workorder/161803/laborDetail
can
be obtained simply by calling document('laborDetail',.)
.
The relative REST URIs in this article use Generative URIs. Generative URIs have an understandable structure that can be used to build URIs to resources, for which you do not already have an explicit link. This is in contrast to opaque URIs, which require an index, search service, or some other link topology in order to be known.
Even though generative URIs can be very useful in developing ad-hoc relationships between resources and extending services, reliance on generative URIs is justifiably controversial. A good reason for this controversy is that reliance on generative URIs is only as robust as the assumption that the URI structure will not have to change.
Putting It on Paper
XSLT can generate a number of output formats that can be used to generate printed output including: Plain Text, HTML, or XML formats that are native to some newer word processing applications. We'll take a closer look at RTF and XSL-FO, which are widely supported, provide control over printed page layout, and allow embedded graphics.
RTF
At first glance, an RTF document appears to
be an evil forest of 80's text markup. It is however, logical, well documented, and
widely
supported by a variety of word processors. Since RTF is entirely text based, you can
generate it directly from XSLT and send it to the client. With IE and MS Office, using
the
application/msword
mime-type will display the RTF directly in the browser.
The RTF specifications, which are bound to the releases of MS Word, are not light reading: Version 1.8 (Word 2003) is the most current at the time of this article, but Version 1.5 (Word 97) is probably the most appropriate to use in practice. Sean Burke's RTF Pocket Guide is a good and gentle introduction to the basics of RTF. Also, you can avoid a lot of RTF coding by building a template document in your favorite word processor, saving it as RTF, and carefully cutting and pasting it into your XSLT templates.
Despite the fact that it is cheap and fast, RTF is seldom my first choice when XSL-FO is also an option. Even though RTF can control printed output with decent precision, complex layouts or data-driven graphics can quickly become very complicated to express. Version issues, limited debugging feedback (malformed RTF often simply causes MS Word to crash), and tricky syntax make RTF a less-than-ideal match for XSLT and REST reporting.
Hello XSL-FO
XSL-FO is a W3C specification purposely designed for the job of generating printed output from XML data. A good introduction to XSL-FO is at Printing from XML: An Introduction to XSL-FO.
XSL-FO is normally not intended to be viewed directly by end-users; rather, a post-processor reads the XSL-FO to produce paper-ready output. Typically, the output of these post-processors is PDF, but it can also be a bitmap, postscript, or even RTF. These post processors are often called Formatters or Formatting Object Processors (FOPs).
Currently XSL-FO enjoys the support of FOPs from a number of organizations and vendors. At the time this article, they include Adobe, Altsoft, Antenna House, Apache, Chive, Ecrion, RenderX, and Visual Programming. Since the input (XSL-FO) and output (PDF) of these FOPs are stable, we are finally in the enviable position of being able to swap vendors to meet our performance, platform, and pricing needs without breaking our reports. Many of these FOPs also support proprietary extensions, such as XML tags for charting or control over PDF specific features like bookmarks.
Images, XSL-FO, and Formatting Object Processors (FOPs)
The XSL-FO specification does not directly deal with graphics. However, many FOPs also support graphics in the form of embedded SVG. SVG content can be exported from a graphics software package and placed in the XSLT, or SVG content can be produced from a data transformation, like a chart from numerical data or a map from GML.
Even though SVG is often associated with vector graphics, it can also be a vehicle for embedding photographs into your reports. Many APIs that serialize database or object data as XML will serialize binary image data as base-64 text. This image data can be embedded in a XSL-FO report by using XSLT and the following approach:
- [1] Wrap the image base-64 text into a URI using the data url scheme.
- [2] Use the
<svg:image>
element to size and transform the image as necessary. - [3] Wrap the SVG document in a
<fo:instream-foreign-object>
element.
<-- Inline image in XSL FO --> <fo:instream-foreign-object [3] content-width="0.8in" content-height="0.6in"> <-- SVG Data --> <svg:svg width="0.8in" height="0.6in" viewBox="0 0 640 480" preserveAspectRatio="xMidYMid" > <-- Embedded JPEG Image --> <svg:image [2] width="640" height="480" transform="rotate(90 320 240)" xlink:href="data:image/jpg;base64,/9j/4AAQ.."[1] /> </svg:svg> </fo:instream-foreign-object>
FOPs vary greatly in their SVG support. If you are shopping for a FOP, and you need graphics support, it is a good idea run a number of large test documents through trial software before you make a purchase.
Getting the Most Out of Your FOP
FOPs often have APIs and connector kits that you can use to tie the FOP directly into your application or publishing framework. However, high-end FOPs are not cheap, and you may want to install your FOP as a service that can be shared by many applications, regardless of their native platform.
A straightforward approach is to use a RESTful FOP service, where you post XSL-FO to the FOP service and get a binary PDF as a response.

Another RESTful approach is to pipeline the XSL-FO through the FOP server. This approach lets you bind a FOP to a service simply by creating a URI.

Consider the URI to the PDF of the example report at the beginning of this article:
http://fopserver/pipe.ashx/workmgmt/workorders/?status=open&xsl=budgetReportFO.xsl
.
The sequence of actions to produce this report would be:
- [1] The client requests the report from the URI
http://fopserver/pipe.ashx/workmgmt/workorders/?status=open& xsl=budgetReportFO.xsl
. - [2] The FOP pipeline service, located at
http://fopserver/pipe.ashx
, uses the remainder of the URI to build a request tohttp://workmgmt/workorders/?status=open&xsl=budgetReportFO.xsl
. - [3] The work managment service applies the
budgetReportFO.xsl
stylesheet to the local data athttp://workmgmt/workorders/?status=open
and responds with XSL-FO data to the FOP server. - [4] The FOP server converts the XSL-FO to PDF and sends it to the client.
A Final Note About Security
Performance will normally be the primary driver in deploying FOPs, Services, and XSLT engines; however, if access to some resources is restriced, security may become the controlling factor in the design of your reporting framework.
When restricted resources are part of a REST report, questions to consider are "How
am I
going to authenticate to the REST resources?" and "Whose credentials should be used?"
Even
though URIs provide a standard method of identifying resources, your resources may
not have
a shared approach for authentication. The document()
function does not provide
a mechanism for basic authentication, and you may have to choose another approach,
such as
using tickets or setting up your URI resolver to preauthenticate or support negotiated
network authentication. Also be aware of whose credentials are being used by the XSLT
engines and URI resolvers, since they may be able to access resources not intended
to be
viewable by the reports' end users.