234 attempt to fix js error Uncaught SyntaxError: Unexpected identifier 'hidden'

This commit is contained in:
2026-04-27 17:57:24 +03:00
parent 8a32a0d5f0
commit 74c073bd7e
149 changed files with 14361 additions and 8886 deletions
+32 -32
View File
@@ -12,7 +12,7 @@ The REST Web Service framework for ColdFusion and Lucee
**Application.cfc:**
```js
```cfscript
component extends="taffy.core.api" {}
```
@@ -24,7 +24,7 @@ component extends="taffy.core.api" {}
**/resources/hello.cfc:**
```js
```cfscript
component extends="taffy.core.resource" taffy_uri="/hello" {
function get(){
@@ -77,13 +77,13 @@ You can see that the taffy folder is a sibling to Application.cfc. This allows A
Next, if your Application.cfc and `/resources/` folder aren't in the web-root (e.g. they're inside something like `/api/`) then you'll need to add an [application-specific mapping](http://livedocs.adobe.com/coldfusion/8/htmldocs/help.html?content=appFramework_04.html) for `/resources` so that Taffy can find your resources to initialize the routes.
```js
```cfscript
this.mappings["/resources"] = expandPath("./resources");
```
You'll also need to add a mapping for `/taffy` so that the resources can extend `taffy.core.resource` (since the taffy folder isn't a child of the resources folder):
```js
```cfscript
this.mappings["/taffy"] = expandPath("./taffy");
```
@@ -132,7 +132,7 @@ In the xml above you can see that I only have 1 wildcard, but to compensate I've
Taffy allows for setting the HTTP status message using [.withStatus()](#withstatus), such as:
```js
```cfscript
return representationOf({...}).withStatus(403, "Not Authorized");
```
@@ -221,7 +221,7 @@ If you want a resource not to be included in the dashboard, set `taffy:dashboard
or
```js
```cfscript
component taffy_uri="/secret-squirrel" taffy_dashboard_hide {
}
```
@@ -247,7 +247,7 @@ The **taffy:uri** property applies to the `<cfcomponent>` tag or the `component{
or
```js
```cfscript
component taffy_uri="/artist/{artistId}" {
}
```
@@ -278,7 +278,7 @@ By convention, resources will automatically map the 4 primary HTTP REST verbs --
or
```js
```cfscript
function getUser( numeric userId ) taffy_verb="get" {
}
```
@@ -294,7 +294,7 @@ You can prevent a resource from showing in the documentation by adding the `taff
or
```js
```cfscript
component extends="taffy.core.resource" taffy_uri="/artist/{artistId}" taffy_docs_hide {
}
```
@@ -310,7 +310,7 @@ Similarly, you can hide methods and parameters by adding the attribute to the `<
or
```js
```cfscript
public function getUser(
required numeric userId,
string _hidden = "" taffy_docs_hide
@@ -335,7 +335,7 @@ By convention, the mime-types supported by your API are determined by the method
or
```js
```cfscript
function getAsJson() taffy_mime="application/json" {
}
```
@@ -354,7 +354,7 @@ When your API supports more than one data format (i.e. json and xml), you must s
or
```js
```cfscript
function getAsJson() taffy_mime="application/json" taffy_default="true" {}
function getAsXml() taffy_mime="application/xml" {}
@@ -364,7 +364,7 @@ function getAsXml() taffy_mime="application/xml" {}
Default values:
```js
```cfscript
variables.framework = {
reloadKey = "reload",
reloadPassword = "true",
@@ -570,7 +570,7 @@ The allowed verbs, of course, are the ones allowed by the requested resource, as
In addition, as of Taffy 3.1.0, you can set this setting to a string of allowable hosts, as a (comma, semicolon, or space) delimited list:
```js
```cfscript
variables.framework.allowCrossDomain =
"http://example.com; http://foo.bar, http://google.com";
```
@@ -637,7 +637,7 @@ Global headers are static. You set them on application initialization and they d
**NOTE FOR EXTERNAL BEAN FACTORY USERS (e.g. Coldspring, DI/1 etc)** If your external bean factory is initialized in onApplicationStart then you need to set the variables.framework.beanFactory after your bean factory has initialized, for example:
```js
```cfscript
component extends="taffy.core.api"
{
this.name = 'myapi';
@@ -710,7 +710,7 @@ Taffy calls this method during initialization to determine in which configured e
The returned value will be used to load environment-specific configuration. For example, if you have the following code in your Application.cfc, then the dashboard will be disabled in production:
```js
```cfscript
variables.framework = {
disableDashboard = false
@@ -769,13 +769,13 @@ This method is optional, and allows you to inspect and potentially abort an API
If you do not return TRUE, allowing the request to continue as normal, then Taffy expects you to call and return the result of either **[noData()](#nodata)** or **[representationOf()](#representationof)**. If you simply want to return with a status code of 403 (which indicates "Not Allowed") and no response body, you could do this:
```js
```cfscript
return noData().withStatus(403);
```
Alternately, you could return some data to indicate that they owe you money or something:
```js
```cfscript
return representationOf({
error="Your account is past due. Please email accounts payable."
})
@@ -786,7 +786,7 @@ The options here are limited only by your imagination.
You can add data to the **requestArguments** structure and this will be passed on to any resource that handles the request. Simply add a key to the structure:
```js
```cfscript
function onTaffyRequest(
verb,
cfc,
@@ -803,7 +803,7 @@ function onTaffyRequest(
In your resource:
```js
```cfscript
function get(myData) {
//arguments.myData => "myValue"
}
@@ -811,13 +811,13 @@ function get(myData) {
You can use the method metadata for anything you see fit; but the original use case was for role-based security. Consider the following resource method:
```js
```cfscript
public function getData( id ) taffy_method="get" role="datareader" { ... }
```
Taffy doesn't do anything with the **role** metadata on this method other than expose it to you in onTaffyRequest. So let's use the user's API key to find out what roles they have, and verify that the method's required role is among them. This is a snippet from your Application.cfc:
```js
```cfscript
function onTaffyRequest(verb, cfc, requestArgs, mime, head, methodMetadata){
local.user = (...); //get user from api key...
@@ -914,7 +914,7 @@ This method saves data in a way that makes it available to [exception log adapte
This is a convenience method used to make sure that certain all-numeric inputs get serialized as a string in the output not converted to numeric output. Examples are postal codes or phone numbers.
```js
```cfscript
return rep(
queryToArray(myQuery, function (row) {
row.phone = encode.string(row.phone);
@@ -938,7 +938,7 @@ Behaves like `noData()` except that it sets the status code to 204 and the Conte
This method allows you to specify that there is no data to be returned for the current request. Generally, you would use it in conjunction with the **withStatus** method to set a specific return status for the request. For example, if the requested resource doesn't exist, you could return a 404 error like so:
```js
```cfscript
return noData().withStatus(404);
```
@@ -956,7 +956,7 @@ This method transforms a ColdFusion query object into an array of structures. It
The callback function can be used to efficiently transform keys in the structure before it is added to the array without additional looping. For example, you can use it to format dates in a particular style. **Your callback must return a value.**
```js
```cfscript
queryToArray(someQ, function (row) {
row.startDate = dateFormat(row.startDate, "yyyy-mm-dd");
return row;
@@ -965,7 +965,7 @@ queryToArray(someQ, function (row) {
When you want to return the resulting array as your API response, you must still wrap it in a [representationOf](#rep-1) call:
```js
```cfscript
return rep(queryToArray(someQ));
```
@@ -983,7 +983,7 @@ This method transforms a ColdFusion query object into a structure. If there is m
The callback function is passed the column name and the value, and can be used to efficiently transform keys in the structure as they are read from the query without need for additional looping. For example, you can use it to format dates in a particular style. **Your callback must return a value.**
```js
```cfscript
return queryToStruct(someQ, function (colName, val) {
if (colName == "startDate") {
return dateFormat(val, "yyyy-mm-dd");
@@ -1031,7 +1031,7 @@ If you don't configure a logging adapter, the default is LogToEmail, but the def
Use this method in place of `representationOf()` to return a stream of binary data. Useful for streaming things like dynamically generated PDFs. **Note: ** When streaming binary data as the response, you must set the mime type manually using [withMime()](#withmime). For example:
```js
```cfscript
return streamBinary(local.pdf).withStatus(200).withMime("application/pdf");
```
@@ -1044,7 +1044,7 @@ return streamBinary(local.pdf).withStatus(200).withMime("application/pdf");
Use this method in place of `representationOf()` to stream a file from disk (or VFS). Optionally append `.andDelete( true )` to delete the file once streaming is complete. **Note: ** When streaming binary data as the response, you must set the mime type manually using [withMime()](#withmime). For example:
```js
```cfscript
return streamFile("/foo.txt")
.andDelete(true)
.withStatus(200)
@@ -1069,7 +1069,7 @@ Use this method in place of `representationOf()` to stream an image from disk (o
This special method _**requires**_ the use of either **noData** or **representationOf**. It adds custom headers to the return. Additional use of **withStatus** optional.
```js
```cfscript
return representationOf(myData).withHeaders({"X-POWERED-BY"="Taffy 2.0!"});
```
@@ -1082,7 +1082,7 @@ return representationOf(myData).withHeaders({"X-POWERED-BY"="Taffy 2.0!"});
This special method _**requires**_ the use of either **streamFile** or **streamBinary**. It overrides the default mime type header for the return.
```js
```cfscript
return streamFile("kittens/cuteness.pdf").withMime("application/pdf");
```
@@ -1098,7 +1098,7 @@ This special method _**requires**_ the use of either **noData** or **representat
_If you do not specify a return status code, Taffy will always return status code 200 (OK) by default._
```js
```cfscript
return noData().withStatus(404, "Not Found");
```
+20 -20
View File
@@ -12,7 +12,7 @@ The REST Web Service framework for ColdFusion and Railo
**Application.cfc:**
```js
```cfscript
component extends="taffy.core.api" {}
```
@@ -24,7 +24,7 @@ component extends="taffy.core.api" {}
**/resources/hello.cfc:**
```js
```cfscript
component extends="taffy.core.resource" taffy_uri="/hello" {
function get(){
@@ -77,13 +77,13 @@ You can see that the taffy folder is a sibling to Application.cfc. This allows A
Next, if your Application.cfc and `/resources/` folder aren't in the web-root (e.g. they're inside something like `/api/`) then you'll need to add an [application-specific mapping](http://livedocs.adobe.com/coldfusion/8/htmldocs/help.html?content=appFramework_04.html) for `/resources` so that Taffy can find your resources to initialize the routes.
```js
```cfscript
this.mappings["/resources"] = expandPath("./resources");
```
You'll also need to add a mapping for `/taffy` so that the resources can extend `taffy.core.resource` (since the taffy folder isn't a child of the resources folder):
```js
```cfscript
this.mappings["/taffy"] = expandPath("./taffy");
```
@@ -101,7 +101,7 @@ Implementing OAuth is something I would like to document, but it is a fairly lar
Taffy allows for setting the HTTP status message using [.withStatus()](#withstatus), such as:
```js
```cfscript
return representationOf({...}).withStatus(403, "Not Authorized");
```
@@ -148,7 +148,7 @@ The **taffy:uri** property applies to the `<cfcomponent>` tag or the `component{
or
```js
```cfscript
component taffy_uri="/artist/{artistId}" {
}
```
@@ -179,7 +179,7 @@ By convention, resources will automatically map the 4 primary HTTP REST verbs --
or
```js
```cfscript
function getUser( numeric userId ) taffy_verb="get" {
}
```
@@ -199,7 +199,7 @@ By convention, the mime-types supported by your API are determined by the method
or
```js
```cfscript
function getAsJson() taffy_mime="application/json" {
}
```
@@ -218,7 +218,7 @@ When your API supports more than one data format (i.e. json and xml), you must s
or
```js
```cfscript
function getAsJson() taffy_mime="application/json" taffy_default="true" {}
function getAsXml() taffy_mime="application/xml" {}
@@ -228,7 +228,7 @@ function getAsXml() taffy_mime="application/xml" {}
Default values:
```js
```cfscript
variables.framework = {
reloadKey = "reload",
reloadPassword = "true",
@@ -453,7 +453,7 @@ Taffy calls this method during initialization to determine in which configured e
The returned value will be used to load environment-specific configuration. For example, if you have the following code in your Application.cfc, then the dashboard will be disabled in production:
```js
```cfscript
variables.framework = {
disableDashboard = false
@@ -488,13 +488,13 @@ This method is optional, and allows you to inspect and potentially abort an API
If you do not return TRUE, allowing the request to continue as normal, then Taffy expects you to return a **[representation](/atuttle/Taffy/wiki/Using-a-Custom-Representation-Class)** (either the default class, or a custom one) that it should immediately return to the consumer, serialized to the appropriate format. If you simply want to return with a status code of 403 (which indicates "Not Allowed"), you could do this:
```js
```cfscript
return newRepresentation().noData().withStatus(403);
```
Alternately, you could return some data to indicate that they owe you money or something:
```js
```cfscript
return newRepresentation()
.setData({error="Your account is past due. Please email accounts payable."})
.withStatus(403);
@@ -504,7 +504,7 @@ The options here are limited only by your imagination.
You can add data to the **requestArguments** structure and this will be passed on to any resource that handles the request. Simply add a key to the structure:
```js
```cfscript
function onTaffyRequest(verb, cfc, requestArguments, mimeExt, headers) {
arguments.requestArguments.myData = "myvalue";
return true;
@@ -513,7 +513,7 @@ function onTaffyRequest(verb, cfc, requestArguments, mimeExt, headers) {
In your resource:
```js
```cfscript
function get(myData) {
//arguments.myData = myValue
}
@@ -530,7 +530,7 @@ Resource CFCs extend `taffy.core.resource`. The following methods are available
This method allows you to specify that there is no data to be returned for the current request. Generally, you would use it in conjunction with the **withStatus** method to set a specific return status for the request. For example, if the requested resource doesn't exist, you could return a 404 error like so:
```js
```cfscript
return noData().withStatus(404);
```
@@ -543,7 +543,7 @@ return noData().withStatus(404);
This method transforms a ColdFusion query object into an array of structures. It was added because ColdFusion's serializeJSON functionality uses an ..._eccentric_... format for queries. **queryToArray** returns the format most people expect: a vanilla array of structures with named keys. To be fair the ACF serialization format uses less data as long as there is more than 1 row in the query, but it doesn't matter that you do a better job if nobody understands your output. _queryToArray also preserves query column name case, which serializeJSON does not._
```js
```cfscript
return representationOf(queryToArray(someQuery));
```
@@ -603,7 +603,7 @@ Use this method in place of `representationOf()` to stream an image from disk (o
This special method _**requires**_ the use of either **noData** or **representationOf**. It adds custom headers to the return. Additional use of **withStatus** optional.
```js
```cfscript
return representationOf(myData).withHeaders({"X-POWERED-BY"="Taffy 2.0!"});
```
@@ -616,7 +616,7 @@ return representationOf(myData).withHeaders({"X-POWERED-BY"="Taffy 2.0!"});
This special method _**requires**_ the use of either **streamFile** or **streamBinary**. It overrides the default mime type header for the return.
```js
```cfscript
return streamFile("kittens/cuteness.pdf").withMime("application/pdf");
```
@@ -632,7 +632,7 @@ This special method _**requires**_ the use of either **noData** or **representat
_If you do not specify a return status code, Taffy will always return status code 200 (OK) by default._
```js
```cfscript
return noData().withStatus(404, "Not Found");
```
+20 -20
View File
@@ -12,7 +12,7 @@ The REST Web Service framework for ColdFusion and Railo
**Application.cfc:**
```js
```cfscript
component extends="taffy.core.api" {}
```
@@ -24,7 +24,7 @@ component extends="taffy.core.api" {}
**/resources/hello.cfc:**
```js
```cfscript
component extends="taffy.core.resource" taffy_uri="/hello" {
function get(){
@@ -77,13 +77,13 @@ You can see that the taffy folder is a sibling to Application.cfc. This allows A
Next, if your Application.cfc and `/resources/` folder aren't in the web-root (e.g. they're inside something like `/api/`) then you'll need to add an [application-specific mapping](http://livedocs.adobe.com/coldfusion/8/htmldocs/help.html?content=appFramework_04.html) for `/resources` so that Taffy can find your resources to initialize the routes.
```js
```cfscript
this.mappings["/resources"] = expandPath("./resources");
```
You'll also need to add a mapping for `/taffy` so that the resources can extend `taffy.core.resource` (since the taffy folder isn't a child of the resources folder):
```js
```cfscript
this.mappings["/taffy"] = expandPath("./taffy");
```
@@ -101,7 +101,7 @@ Implementing OAuth is something I would like to document, but it is a fairly lar
Taffy allows for setting the HTTP status message using [.withStatus()](#withstatus), such as:
```js
```cfscript
return representationOf({...}).withStatus(403, "Not Authorized");
```
@@ -148,7 +148,7 @@ The **taffy:uri** property applies to the `<cfcomponent>` tag or the `component{
or
```js
```cfscript
component taffy_uri="/artist/{artistId}" {
}
```
@@ -179,7 +179,7 @@ By convention, resources will automatically map the 4 primary HTTP REST verbs --
or
```js
```cfscript
function getUser( numeric userId ) taffy_verb="get" {
}
```
@@ -199,7 +199,7 @@ By convention, the mime-types supported by your API are determined by the method
or
```js
```cfscript
function getAsJson() taffy_mime="application/json" {
}
```
@@ -218,7 +218,7 @@ When your API supports more than one data format (i.e. json and xml), you must s
or
```js
```cfscript
function getAsJson() taffy_mime="application/json" taffy_default="true" {}
function getAsXml() taffy_mime="application/xml" {}
@@ -228,7 +228,7 @@ function getAsXml() taffy_mime="application/xml" {}
Default values:
```js
```cfscript
variables.framework = {
reloadKey = "reload",
reloadPassword = "true",
@@ -453,7 +453,7 @@ Taffy calls this method during initialization to determine in which configured e
The returned value will be used to load environment-specific configuration. For example, if you have the following code in your Application.cfc, then the dashboard will be disabled in production:
```js
```cfscript
variables.framework = {
disableDashboard = false
@@ -488,13 +488,13 @@ This method is optional, and allows you to inspect and potentially abort an API
If you do not return TRUE, allowing the request to continue as normal, then Taffy expects you to return a **[representation](/atuttle/Taffy/wiki/Using-a-Custom-Representation-Class)** (either the default class, or a custom one) that it should immediately return to the consumer, serialized to the appropriate format. If you simply want to return with a status code of 403 (which indicates "Not Allowed"), you could do this:
```js
```cfscript
return newRepresentation().noData().withStatus(403);
```
Alternately, you could return some data to indicate that they owe you money or something:
```js
```cfscript
return newRepresentation()
.setData({error="Your account is past due. Please email accounts payable."})
.withStatus(403);
@@ -504,7 +504,7 @@ The options here are limited only by your imagination.
You can add data to the **requestArguments** structure and this will be passed on to any resource that handles the request. Simply add a key to the structure:
```js
```cfscript
function onTaffyRequest(verb, cfc, requestArguments, mimeExt, headers) {
arguments.requestArguments.myData = "myvalue";
return true;
@@ -513,7 +513,7 @@ function onTaffyRequest(verb, cfc, requestArguments, mimeExt, headers) {
In your resource:
```js
```cfscript
function get(myData) {
//arguments.myData = myValue
}
@@ -530,7 +530,7 @@ Resource CFCs extend `taffy.core.resource`. The following methods are available
This method allows you to specify that there is no data to be returned for the current request. Generally, you would use it in conjunction with the **withStatus** method to set a specific return status for the request. For example, if the requested resource doesn't exist, you could return a 404 error like so:
```js
```cfscript
return noData().withStatus(404);
```
@@ -543,7 +543,7 @@ return noData().withStatus(404);
This method transforms a ColdFusion query object into an array of structures. It was added because ColdFusion's serializeJSON functionality uses an ..._eccentric_... format for queries. **queryToArray** returns the format most people expect: a vanilla array of structures with named keys. To be fair the ACF serialization format uses less data as long as there is more than 1 row in the query, but it doesn't matter that you do a better job if nobody understands your output. _queryToArray also preserves query column name case, which serializeJSON does not._
```js
```cfscript
return representationOf(queryToArray(someQuery));
```
@@ -603,7 +603,7 @@ Use this method in place of `representationOf()` to stream an image from disk (o
This special method _**requires**_ the use of either **noData** or **representationOf**. It adds custom headers to the return. Additional use of **withStatus** optional.
```js
```cfscript
return representationOf(myData).withHeaders({"X-POWERED-BY"="Taffy 2.0!"});
```
@@ -616,7 +616,7 @@ return representationOf(myData).withHeaders({"X-POWERED-BY"="Taffy 2.0!"});
This special method _**requires**_ the use of either **streamFile** or **streamBinary**. It overrides the default mime type header for the return.
```js
```cfscript
return streamFile("kittens/cuteness.pdf").withMime("application/pdf");
```
@@ -632,7 +632,7 @@ This special method _**requires**_ the use of either **noData** or **representat
_If you do not specify a return status code, Taffy will always return status code 200 (OK) by default._
```js
```cfscript
return noData().withStatus(404, "Not Found");
```
+23 -23
View File
@@ -12,7 +12,7 @@ The REST Web Service framework for ColdFusion and Railo
**Application.cfc:**
```js
```cfscript
component extends="taffy.core.api" {}
```
@@ -24,7 +24,7 @@ component extends="taffy.core.api" {}
**/resources/hello.cfc:**
```js
```cfscript
component extends="taffy.core.resource" taffy_uri="/hello" {
function get(){
@@ -77,13 +77,13 @@ You can see that the taffy folder is a sibling to Application.cfc. This allows A
Next, if your Application.cfc and `/resources/` folder aren't in the web-root (e.g. they're inside something like `/api/`) then you'll need to add an [application-specific mapping](http://livedocs.adobe.com/coldfusion/8/htmldocs/help.html?content=appFramework_04.html) for `/resources` so that Taffy can find your resources to initialize the routes.
```js
```cfscript
this.mappings["/resources"] = expandPath("./resources");
```
You'll also need to add a mapping for `/taffy` so that the resources can extend `taffy.core.resource` (since the taffy folder isn't a child of the resources folder):
```js
```cfscript
this.mappings["/taffy"] = expandPath("./taffy");
```
@@ -128,7 +128,7 @@ In the xml above you can see that I only have 1 wildcard, but to compensate I've
Taffy allows for setting the HTTP status message using [.withStatus()](#withstatus), such as:
```js
```cfscript
return representationOf({...}).withStatus(403, "Not Authorized");
```
@@ -196,7 +196,7 @@ The **taffy:uri** property applies to the `<cfcomponent>` tag or the `component{
or
```js
```cfscript
component taffy_uri="/artist/{artistId}" {
}
```
@@ -227,7 +227,7 @@ By convention, resources will automatically map the 4 primary HTTP REST verbs --
or
```js
```cfscript
function getUser( numeric userId ) taffy_verb="get" {
}
```
@@ -247,7 +247,7 @@ By convention, the mime-types supported by your API are determined by the method
or
```js
```cfscript
function getAsJson() taffy_mime="application/json" {
}
```
@@ -266,7 +266,7 @@ When your API supports more than one data format (i.e. json and xml), you must s
or
```js
```cfscript
function getAsJson() taffy_mime="application/json" taffy_default="true" {}
function getAsXml() taffy_mime="application/xml" {}
@@ -276,7 +276,7 @@ function getAsXml() taffy_mime="application/xml" {}
Default values:
```js
```cfscript
variables.framework = {
reloadKey = "reload",
reloadPassword = "true",
@@ -501,7 +501,7 @@ Taffy calls this method during initialization to determine in which configured e
The returned value will be used to load environment-specific configuration. For example, if you have the following code in your Application.cfc, then the dashboard will be disabled in production:
```js
```cfscript
variables.framework = {
disableDashboard = false
@@ -537,13 +537,13 @@ This method is optional, and allows you to inspect and potentially abort an API
If you do not return TRUE, allowing the request to continue as normal, then Taffy expects you to return a **[representation](#custom-representation-classes)** (either the default class, or a custom one) that it should immediately return to the consumer, serialized to the appropriate format. If you simply want to return with a status code of 403 (which indicates "Not Allowed"), you could do this:
```js
```cfscript
return newRepresentation().noData().withStatus(403);
```
Alternately, you could return some data to indicate that they owe you money or something:
```js
```cfscript
return newRepresentation()
.setData({error="Your account is past due. Please email accounts payable."})
.withStatus(403);
@@ -553,7 +553,7 @@ The options here are limited only by your imagination.
You can add data to the **requestArguments** structure and this will be passed on to any resource that handles the request. Simply add a key to the structure:
```js
```cfscript
function onTaffyRequest(verb, cfc, requestArguments, mimeExt, headers) {
arguments.requestArguments.myData = "myvalue";
return true;
@@ -562,7 +562,7 @@ function onTaffyRequest(verb, cfc, requestArguments, mimeExt, headers) {
In your resource:
```js
```cfscript
function get(myData) {
//arguments.myData => "myValue"
}
@@ -570,13 +570,13 @@ function get(myData) {
You can use the method metadata for anything you see fit; but the original use case was for role-based security. Consider the following resource method:
```js
```cfscript
public function getData( id ) taffy_method="get" role="datareader" { ... }
```
Taffy doesn't do anything with the **role** metadata on this method other than expose it to you in onTaffyRequest. So let's use the user's API key to find out what roles they have, and verify that the method's required role is among them. This is a snippet from your Application.cfc:
```js
```cfscript
function onTaffyRequest(verb, cfc, requestArgs, mime, head, methodMetadata){
local.user = (...); //get user from api key...
@@ -606,7 +606,7 @@ Resource CFCs extend `taffy.core.resource`. The following methods are available
This method allows you to specify that there is no data to be returned for the current request. Generally, you would use it in conjunction with the **withStatus** method to set a specific return status for the request. For example, if the requested resource doesn't exist, you could return a 404 error like so:
```js
```cfscript
return noData().withStatus(404);
```
@@ -619,7 +619,7 @@ return noData().withStatus(404);
This method transforms a ColdFusion query object into an array of structures. It was added because ColdFusion's serializeJSON functionality uses an ..._eccentric_... format for queries. **queryToArray** returns the format most people expect: a vanilla array of structures with named keys. To be fair the ACF serialization format uses less data as long as there is more than 1 row in the query, but it doesn't matter that you do a better job if nobody understands your output. _queryToArray also preserves query column name case, which serializeJSON does not._
```js
```cfscript
return representationOf(queryToArray(someQuery));
```
@@ -661,7 +661,7 @@ Use this method in place of `representationOf()` to return a stream of binary da
Use this method in place of `representationOf()` to stream a file from disk (or VFS). Optionally append `.andDelete( true )` to delete the file once streaming is complete.
```js
```cfscript
return streamFile("/foo.txt").andDelete(true);
```
@@ -683,7 +683,7 @@ Use this method in place of `representationOf()` to stream an image from disk (o
This special method _**requires**_ the use of either **noData** or **representationOf**. It adds custom headers to the return. Additional use of **withStatus** optional.
```js
```cfscript
return representationOf(myData).withHeaders({"X-POWERED-BY"="Taffy 2.0!"});
```
@@ -696,7 +696,7 @@ return representationOf(myData).withHeaders({"X-POWERED-BY"="Taffy 2.0!"});
This special method _**requires**_ the use of either **streamFile** or **streamBinary**. It overrides the default mime type header for the return.
```js
```cfscript
return streamFile("kittens/cuteness.pdf").withMime("application/pdf");
```
@@ -712,7 +712,7 @@ This special method _**requires**_ the use of either **noData** or **representat
_If you do not specify a return status code, Taffy will always return status code 200 (OK) by default._
```js
```cfscript
return noData().withStatus(404, "Not Found");
```
+23 -23
View File
@@ -12,7 +12,7 @@ The REST Web Service framework for ColdFusion and Railo
**Application.cfc:**
```js
```cfscript
component extends="taffy.core.api" {}
```
@@ -24,7 +24,7 @@ component extends="taffy.core.api" {}
**/resources/hello.cfc:**
```js
```cfscript
component extends="taffy.core.resource" taffy_uri="/hello" {
function get(){
@@ -77,13 +77,13 @@ You can see that the taffy folder is a sibling to Application.cfc. This allows A
Next, if your Application.cfc and `/resources/` folder aren't in the web-root (e.g. they're inside something like `/api/`) then you'll need to add an [application-specific mapping](http://livedocs.adobe.com/coldfusion/8/htmldocs/help.html?content=appFramework_04.html) for `/resources` so that Taffy can find your resources to initialize the routes.
```js
```cfscript
this.mappings["/resources"] = expandPath("./resources");
```
You'll also need to add a mapping for `/taffy` so that the resources can extend `taffy.core.resource` (since the taffy folder isn't a child of the resources folder):
```js
```cfscript
this.mappings["/taffy"] = expandPath("./taffy");
```
@@ -132,7 +132,7 @@ In the xml above you can see that I only have 1 wildcard, but to compensate I've
Taffy allows for setting the HTTP status message using [.withStatus()](#withstatus), such as:
```js
```cfscript
return representationOf({...}).withStatus(403, "Not Authorized");
```
@@ -200,7 +200,7 @@ The **taffy:uri** property applies to the `<cfcomponent>` tag or the `component{
or
```js
```cfscript
component taffy_uri="/artist/{artistId}" {
}
```
@@ -231,7 +231,7 @@ By convention, resources will automatically map the 4 primary HTTP REST verbs --
or
```js
```cfscript
function getUser( numeric userId ) taffy_verb="get" {
}
```
@@ -251,7 +251,7 @@ By convention, the mime-types supported by your API are determined by the method
or
```js
```cfscript
function getAsJson() taffy_mime="application/json" {
}
```
@@ -270,7 +270,7 @@ When your API supports more than one data format (i.e. json and xml), you must s
or
```js
```cfscript
function getAsJson() taffy_mime="application/json" taffy_default="true" {}
function getAsXml() taffy_mime="application/xml" {}
@@ -280,7 +280,7 @@ function getAsXml() taffy_mime="application/xml" {}
Default values:
```js
```cfscript
variables.framework = {
reloadKey = "reload",
reloadPassword = "true",
@@ -505,7 +505,7 @@ Taffy calls this method during initialization to determine in which configured e
The returned value will be used to load environment-specific configuration. For example, if you have the following code in your Application.cfc, then the dashboard will be disabled in production:
```js
```cfscript
variables.framework = {
disableDashboard = false
@@ -541,13 +541,13 @@ This method is optional, and allows you to inspect and potentially abort an API
If you do not return TRUE, allowing the request to continue as normal, then Taffy expects you to return a **[representation](#custom-representation-classes)** (either the default class, or a custom one) that it should immediately return to the consumer, serialized to the appropriate format. If you simply want to return with a status code of 403 (which indicates "Not Allowed"), you could do this:
```js
```cfscript
return newRepresentation().noData().withStatus(403);
```
Alternately, you could return some data to indicate that they owe you money or something:
```js
```cfscript
return newRepresentation()
.setData({error="Your account is past due. Please email accounts payable."})
.withStatus(403);
@@ -557,7 +557,7 @@ The options here are limited only by your imagination.
You can add data to the **requestArguments** structure and this will be passed on to any resource that handles the request. Simply add a key to the structure:
```js
```cfscript
function onTaffyRequest(verb, cfc, requestArguments, mimeExt, headers) {
arguments.requestArguments.myData = "myvalue";
return true;
@@ -566,7 +566,7 @@ function onTaffyRequest(verb, cfc, requestArguments, mimeExt, headers) {
In your resource:
```js
```cfscript
function get(myData) {
//arguments.myData => "myValue"
}
@@ -574,13 +574,13 @@ function get(myData) {
You can use the method metadata for anything you see fit; but the original use case was for role-based security. Consider the following resource method:
```js
```cfscript
public function getData( id ) taffy_method="get" role="datareader" { ... }
```
Taffy doesn't do anything with the **role** metadata on this method other than expose it to you in onTaffyRequest. So let's use the user's API key to find out what roles they have, and verify that the method's required role is among them. This is a snippet from your Application.cfc:
```js
```cfscript
function onTaffyRequest(verb, cfc, requestArgs, mime, head, methodMetadata){
local.user = (...); //get user from api key...
@@ -610,7 +610,7 @@ Resource CFCs extend `taffy.core.resource`. The following methods are available
This method allows you to specify that there is no data to be returned for the current request. Generally, you would use it in conjunction with the **withStatus** method to set a specific return status for the request. For example, if the requested resource doesn't exist, you could return a 404 error like so:
```js
```cfscript
return noData().withStatus(404);
```
@@ -623,7 +623,7 @@ return noData().withStatus(404);
This method transforms a ColdFusion query object into an array of structures. It was added because ColdFusion's serializeJSON functionality uses an ..._eccentric_... format for queries. **queryToArray** returns the format most people expect: a vanilla array of structures with named keys. To be fair the ACF serialization format uses less data as long as there is more than 1 row in the query, but it doesn't matter that you do a better job if nobody understands your output. _queryToArray also preserves query column name case, which serializeJSON does not._
```js
```cfscript
return representationOf(queryToArray(someQuery));
```
@@ -665,7 +665,7 @@ Use this method in place of `representationOf()` to return a stream of binary da
Use this method in place of `representationOf()` to stream a file from disk (or VFS). Optionally append `.andDelete( true )` to delete the file once streaming is complete.
```js
```cfscript
return streamFile("/foo.txt").andDelete(true);
```
@@ -687,7 +687,7 @@ Use this method in place of `representationOf()` to stream an image from disk (o
This special method _**requires**_ the use of either **noData** or **representationOf**. It adds custom headers to the return. Additional use of **withStatus** optional.
```js
```cfscript
return representationOf(myData).withHeaders({"X-POWERED-BY"="Taffy 2.0!"});
```
@@ -700,7 +700,7 @@ return representationOf(myData).withHeaders({"X-POWERED-BY"="Taffy 2.0!"});
This special method _**requires**_ the use of either **streamFile** or **streamBinary**. It overrides the default mime type header for the return.
```js
```cfscript
return streamFile("kittens/cuteness.pdf").withMime("application/pdf");
```
@@ -716,7 +716,7 @@ This special method _**requires**_ the use of either **noData** or **representat
_If you do not specify a return status code, Taffy will always return status code 200 (OK) by default._
```js
```cfscript
return noData().withStatus(404, "Not Found");
```
+22 -22
View File
@@ -12,7 +12,7 @@ The REST Web Service framework for ColdFusion and Railo
**Application.cfc:**
```js
```cfscript
component extends="taffy.core.api" {}
```
@@ -24,7 +24,7 @@ component extends="taffy.core.api" {}
**/resources/hello.cfc:**
```js
```cfscript
component extends="taffy.core.resource" taffy_uri="/hello" {
function get(){
@@ -77,13 +77,13 @@ You can see that the taffy folder is a sibling to Application.cfc. This allows A
Next, if your Application.cfc and `/resources/` folder aren't in the web-root (e.g. they're inside something like `/api/`) then you'll need to add an [application-specific mapping](http://livedocs.adobe.com/coldfusion/8/htmldocs/help.html?content=appFramework_04.html) for `/resources` so that Taffy can find your resources to initialize the routes.
```js
```cfscript
this.mappings["/resources"] = expandPath("./resources");
```
You'll also need to add a mapping for `/taffy` so that the resources can extend `taffy.core.resource` (since the taffy folder isn't a child of the resources folder):
```js
```cfscript
this.mappings["/taffy"] = expandPath("./taffy");
```
@@ -132,7 +132,7 @@ In the xml above you can see that I only have 1 wildcard, but to compensate I've
Taffy allows for setting the HTTP status message using [.withStatus()](#withstatus), such as:
```js
```cfscript
return representationOf({...}).withStatus(403, "Not Authorized");
```
@@ -200,7 +200,7 @@ The **taffy:uri** property applies to the `<cfcomponent>` tag or the `component{
or
```js
```cfscript
component taffy_uri="/artist/{artistId}" {
}
```
@@ -231,7 +231,7 @@ By convention, resources will automatically map the 4 primary HTTP REST verbs --
or
```js
```cfscript
function getUser( numeric userId ) taffy_verb="get" {
}
```
@@ -251,7 +251,7 @@ By convention, the mime-types supported by your API are determined by the method
or
```js
```cfscript
function getAsJson() taffy_mime="application/json" {
}
```
@@ -270,7 +270,7 @@ When your API supports more than one data format (i.e. json and xml), you must s
or
```js
```cfscript
function getAsJson() taffy_mime="application/json" taffy_default="true" {}
function getAsXml() taffy_mime="application/xml" {}
@@ -280,7 +280,7 @@ function getAsXml() taffy_mime="application/xml" {}
Default values:
```js
```cfscript
variables.framework = {
reloadKey = "reload",
reloadPassword = "true",
@@ -500,7 +500,7 @@ Taffy calls this method during initialization to determine in which configured e
The returned value will be used to load environment-specific configuration. For example, if you have the following code in your Application.cfc, then the dashboard will be disabled in production:
```js
```cfscript
variables.framework = {
disableDashboard = false
@@ -536,13 +536,13 @@ This method is optional, and allows you to inspect and potentially abort an API
If you do not return TRUE, allowing the request to continue as normal, then Taffy expects you to return a **[representation](#custom-representation-classes)** (either the default class, or a custom one) that it should immediately return to the consumer, serialized to the appropriate format. If you simply want to return with a status code of 403 (which indicates "Not Allowed"), you could do this:
```js
```cfscript
return newRepresentation().noData().withStatus(403);
```
Alternately, you could return some data to indicate that they owe you money or something:
```js
```cfscript
return newRepresentation()
.setData({error="Your account is past due. Please email accounts payable."})
.withStatus(403);
@@ -552,7 +552,7 @@ The options here are limited only by your imagination.
You can add data to the **requestArguments** structure and this will be passed on to any resource that handles the request. Simply add a key to the structure:
```js
```cfscript
function onTaffyRequest(verb, cfc, requestArguments, mimeExt, headers) {
arguments.requestArguments.myData = "myvalue";
return true;
@@ -561,7 +561,7 @@ function onTaffyRequest(verb, cfc, requestArguments, mimeExt, headers) {
In your resource:
```js
```cfscript
function get(myData) {
//arguments.myData => "myValue"
}
@@ -569,13 +569,13 @@ function get(myData) {
You can use the method metadata for anything you see fit; but the original use case was for role-based security. Consider the following resource method:
```js
```cfscript
public function getData( id ) taffy_method="get" role="datareader" { ... }
```
Taffy doesn't do anything with the **role** metadata on this method other than expose it to you in onTaffyRequest. So let's use the user's API key to find out what roles they have, and verify that the method's required role is among them. This is a snippet from your Application.cfc:
```js
```cfscript
function onTaffyRequest(verb, cfc, requestArgs, mime, head, methodMetadata){
local.user = (...); //get user from api key...
@@ -605,7 +605,7 @@ Resource CFCs extend `taffy.core.resource`. The following methods are available
This method allows you to specify that there is no data to be returned for the current request. Generally, you would use it in conjunction with the **withStatus** method to set a specific return status for the request. For example, if the requested resource doesn't exist, you could return a 404 error like so:
```js
```cfscript
return noData().withStatus(404);
```
@@ -665,7 +665,7 @@ Use this method in place of `representationOf()` to return a stream of binary da
Use this method in place of `representationOf()` to stream a file from disk (or VFS). Optionally append `.andDelete( true )` to delete the file once streaming is complete.
```js
```cfscript
return streamFile("/foo.txt").andDelete(true);
```
@@ -687,7 +687,7 @@ Use this method in place of `representationOf()` to stream an image from disk (o
This special method _**requires**_ the use of either **noData** or **representationOf**. It adds custom headers to the return. Additional use of **withStatus** optional.
```js
```cfscript
return representationOf(myData).withHeaders({"X-POWERED-BY"="Taffy 2.0!"});
```
@@ -700,7 +700,7 @@ return representationOf(myData).withHeaders({"X-POWERED-BY"="Taffy 2.0!"});
This special method _**requires**_ the use of either **streamFile** or **streamBinary**. It overrides the default mime type header for the return.
```js
```cfscript
return streamFile("kittens/cuteness.pdf").withMime("application/pdf");
```
@@ -716,7 +716,7 @@ This special method _**requires**_ the use of either **noData** or **representat
_If you do not specify a return status code, Taffy will always return status code 200 (OK) by default._
```js
```cfscript
return noData().withStatus(404, "Not Found");
```
+22 -22
View File
@@ -12,7 +12,7 @@ The REST Web Service framework for ColdFusion and Railo
**Application.cfc:**
```js
```cfscript
component extends="taffy.core.api" {}
```
@@ -24,7 +24,7 @@ component extends="taffy.core.api" {}
**/resources/hello.cfc:**
```js
```cfscript
component extends="taffy.core.resource" taffy_uri="/hello" {
function get(){
@@ -77,13 +77,13 @@ You can see that the taffy folder is a sibling to Application.cfc. This allows A
Next, if your Application.cfc and `/resources/` folder aren't in the web-root (e.g. they're inside something like `/api/`) then you'll need to add an [application-specific mapping](http://livedocs.adobe.com/coldfusion/8/htmldocs/help.html?content=appFramework_04.html) for `/resources` so that Taffy can find your resources to initialize the routes.
```js
```cfscript
this.mappings["/resources"] = expandPath("./resources");
```
You'll also need to add a mapping for `/taffy` so that the resources can extend `taffy.core.resource` (since the taffy folder isn't a child of the resources folder):
```js
```cfscript
this.mappings["/taffy"] = expandPath("./taffy");
```
@@ -132,7 +132,7 @@ In the xml above you can see that I only have 1 wildcard, but to compensate I've
Taffy allows for setting the HTTP status message using [.withStatus()](#withstatus), such as:
```js
```cfscript
return representationOf({...}).withStatus(403, "Not Authorized");
```
@@ -200,7 +200,7 @@ The **taffy:uri** property applies to the `<cfcomponent>` tag or the `component{
or
```js
```cfscript
component taffy_uri="/artist/{artistId}" {
}
```
@@ -231,7 +231,7 @@ By convention, resources will automatically map the 4 primary HTTP REST verbs --
or
```js
```cfscript
function getUser( numeric userId ) taffy_verb="get" {
}
```
@@ -251,7 +251,7 @@ By convention, the mime-types supported by your API are determined by the method
or
```js
```cfscript
function getAsJson() taffy_mime="application/json" {
}
```
@@ -270,7 +270,7 @@ When your API supports more than one data format (i.e. json and xml), you must s
or
```js
```cfscript
function getAsJson() taffy_mime="application/json" taffy_default="true" {}
function getAsXml() taffy_mime="application/xml" {}
@@ -280,7 +280,7 @@ function getAsXml() taffy_mime="application/xml" {}
Default values:
```js
```cfscript
variables.framework = {
reloadKey = "reload",
reloadPassword = "true",
@@ -500,7 +500,7 @@ Taffy calls this method during initialization to determine in which configured e
The returned value will be used to load environment-specific configuration. For example, if you have the following code in your Application.cfc, then the dashboard will be disabled in production:
```js
```cfscript
variables.framework = {
disableDashboard = false
@@ -542,13 +542,13 @@ This method is optional, and allows you to inspect and potentially abort an API
If you do not return TRUE, allowing the request to continue as normal, then Taffy expects you to return a **[representation](#custom-representation-classes)** (either the default class, or a custom one) that it should immediately return to the consumer, serialized to the appropriate format. If you simply want to return with a status code of 403 (which indicates "Not Allowed"), you could do this:
```js
```cfscript
return newRepresentation().noData().withStatus(403);
```
Alternately, you could return some data to indicate that they owe you money or something:
```js
```cfscript
return newRepresentation()
.setData({error="Your account is past due. Please email accounts payable."})
.withStatus(403);
@@ -558,7 +558,7 @@ The options here are limited only by your imagination.
You can add data to the **requestArguments** structure and this will be passed on to any resource that handles the request. Simply add a key to the structure:
```js
```cfscript
function onTaffyRequest(verb, cfc, requestArguments, mimeExt, headers) {
arguments.requestArguments.myData = "myvalue";
return true;
@@ -567,7 +567,7 @@ function onTaffyRequest(verb, cfc, requestArguments, mimeExt, headers) {
In your resource:
```js
```cfscript
function get(myData) {
//arguments.myData => "myValue"
}
@@ -575,13 +575,13 @@ function get(myData) {
You can use the method metadata for anything you see fit; but the original use case was for role-based security. Consider the following resource method:
```js
```cfscript
public function getData( id ) taffy_method="get" role="datareader" { ... }
```
Taffy doesn't do anything with the **role** metadata on this method other than expose it to you in onTaffyRequest. So let's use the user's API key to find out what roles they have, and verify that the method's required role is among them. This is a snippet from your Application.cfc:
```js
```cfscript
function onTaffyRequest(verb, cfc, requestArgs, mime, head, methodMetadata){
local.user = (...); //get user from api key...
@@ -611,7 +611,7 @@ Resource CFCs extend `taffy.core.resource`. The following methods are available
This method allows you to specify that there is no data to be returned for the current request. Generally, you would use it in conjunction with the **withStatus** method to set a specific return status for the request. For example, if the requested resource doesn't exist, you could return a 404 error like so:
```js
```cfscript
return noData().withStatus(404);
```
@@ -671,7 +671,7 @@ Use this method in place of `representationOf()` to return a stream of binary da
Use this method in place of `representationOf()` to stream a file from disk (or VFS). Optionally append `.andDelete( true )` to delete the file once streaming is complete.
```js
```cfscript
return streamFile("/foo.txt").andDelete(true);
```
@@ -693,7 +693,7 @@ Use this method in place of `representationOf()` to stream an image from disk (o
This special method _**requires**_ the use of either **noData** or **representationOf**. It adds custom headers to the return. Additional use of **withStatus** optional.
```js
```cfscript
return representationOf(myData).withHeaders({"X-POWERED-BY"="Taffy 2.0!"});
```
@@ -706,7 +706,7 @@ return representationOf(myData).withHeaders({"X-POWERED-BY"="Taffy 2.0!"});
This special method _**requires**_ the use of either **streamFile** or **streamBinary**. It overrides the default mime type header for the return.
```js
```cfscript
return streamFile("kittens/cuteness.pdf").withMime("application/pdf");
```
@@ -722,7 +722,7 @@ This special method _**requires**_ the use of either **noData** or **representat
_If you do not specify a return status code, Taffy will always return status code 200 (OK) by default._
```js
```cfscript
return noData().withStatus(404, "Not Found");
```
+27 -27
View File
@@ -12,7 +12,7 @@ The REST Web Service framework for ColdFusion and Railo
**Application.cfc:**
```js
```cfscript
component extends="taffy.core.api" {}
```
@@ -24,7 +24,7 @@ component extends="taffy.core.api" {}
**/resources/hello.cfc:**
```js
```cfscript
component extends="taffy.core.resource" taffy_uri="/hello" {
function get(){
@@ -77,13 +77,13 @@ You can see that the taffy folder is a sibling to Application.cfc. This allows A
Next, if your Application.cfc and `/resources/` folder aren't in the web-root (e.g. they're inside something like `/api/`) then you'll need to add an [application-specific mapping](http://livedocs.adobe.com/coldfusion/8/htmldocs/help.html?content=appFramework_04.html) for `/resources` so that Taffy can find your resources to initialize the routes.
```js
```cfscript
this.mappings["/resources"] = expandPath("./resources");
```
You'll also need to add a mapping for `/taffy` so that the resources can extend `taffy.core.resource` (since the taffy folder isn't a child of the resources folder):
```js
```cfscript
this.mappings["/taffy"] = expandPath("./taffy");
```
@@ -132,7 +132,7 @@ In the xml above you can see that I only have 1 wildcard, but to compensate I've
Taffy allows for setting the HTTP status message using [.withStatus()](#withstatus), such as:
```js
```cfscript
return representationOf({...}).withStatus(403, "Not Authorized");
```
@@ -217,7 +217,7 @@ The **taffy:uri** property applies to the `<cfcomponent>` tag or the `component{
or
```js
```cfscript
component taffy_uri="/artist/{artistId}" {
}
```
@@ -252,7 +252,7 @@ By convention, resources will automatically map the 4 primary HTTP REST verbs --
or
```js
```cfscript
function getUser( numeric userId ) taffy_verb="get" {
}
```
@@ -272,7 +272,7 @@ By convention, the mime-types supported by your API are determined by the method
or
```js
```cfscript
function getAsJson() taffy_mime="application/json" {
}
```
@@ -291,7 +291,7 @@ When your API supports more than one data format (i.e. json and xml), you must s
or
```js
```cfscript
function getAsJson() taffy_mime="application/json" taffy_default="true" {}
function getAsXml() taffy_mime="application/xml" {}
@@ -301,7 +301,7 @@ function getAsXml() taffy_mime="application/xml" {}
Default values:
```js
```cfscript
variables.framework = {
reloadKey = "reload",
reloadPassword = "true",
@@ -508,7 +508,7 @@ Global headers are static. You set them on application initialization and they d
**NOTE FOR EXTERNAL BEAN FACTORY USERS (e.g. Coldspring, DI/1 etc)** If your external bean factory is initialized in onApplicationStart then you need to set the variables.framework.beanFactory after your bean factory has initialized, for example:
```js
```cfscript
component extends="taffy.core.api"
{
this.name = 'myapi';
@@ -570,7 +570,7 @@ Taffy calls this method during initialization to determine in which configured e
The returned value will be used to load environment-specific configuration. For example, if you have the following code in your Application.cfc, then the dashboard will be disabled in production:
```js
```cfscript
variables.framework = {
disableDashboard = false
@@ -621,13 +621,13 @@ This method is optional, and allows you to inspect and potentially abort an API
If you do not return TRUE, allowing the request to continue as normal, then Taffy expects you to call and return the result of either **[noData()](#nodata)** or **[representationOf()](#representationof)**. If you simply want to return with a status code of 403 (which indicates "Not Allowed") and no response body, you could do this:
```js
```cfscript
return noData().withStatus(403);
```
Alternately, you could return some data to indicate that they owe you money or something:
```js
```cfscript
return representationOf({
error="Your account is past due. Please email accounts payable."
})
@@ -638,7 +638,7 @@ The options here are limited only by your imagination.
You can add data to the **requestArguments** structure and this will be passed on to any resource that handles the request. Simply add a key to the structure:
```js
```cfscript
function onTaffyRequest(
verb,
cfc,
@@ -655,7 +655,7 @@ function onTaffyRequest(
In your resource:
```js
```cfscript
function get(myData) {
//arguments.myData => "myValue"
}
@@ -663,13 +663,13 @@ function get(myData) {
You can use the method metadata for anything you see fit; but the original use case was for role-based security. Consider the following resource method:
```js
```cfscript
public function getData( id ) taffy_method="get" role="datareader" { ... }
```
Taffy doesn't do anything with the **role** metadata on this method other than expose it to you in onTaffyRequest. So let's use the user's API key to find out what roles they have, and verify that the method's required role is among them. This is a snippet from your Application.cfc:
```js
```cfscript
function onTaffyRequest(verb, cfc, requestArgs, mime, head, methodMetadata){
local.user = (...); //get user from api key...
@@ -736,7 +736,7 @@ Resource CFCs extend `taffy.core.resource`. The following methods are available
This method allows you to specify that there is no data to be returned for the current request. Generally, you would use it in conjunction with the **withStatus** method to set a specific return status for the request. For example, if the requested resource doesn't exist, you could return a 404 error like so:
```js
```cfscript
return noData().withStatus(404);
```
@@ -752,7 +752,7 @@ This method transforms a ColdFusion query object into an array of structures. It
The callback function can be used to efficiently transform keys in the structure before it is added to the array without additional looping. For example, you can use it to format dates in a particular style. **Your callback must return a value.**
```js
```cfscript
return queryToArray(someQ, function (row) {
row.startDate = dateFormat(row.startDate, "yyyy-mm-dd");
return row;
@@ -773,7 +773,7 @@ This method transforms a ColdFusion query object into a structure. If there is m
The callback function is passed the column name and the value, and can be used to efficiently transform keys in the structure as they are read from the query without need for additional looping. For example, you can use it to format dates in a particular style. **Your callback must return a value.**
```js
```cfscript
return queryToStruct(someQ, function (colName, val) {
if (colName == "startDate") {
return dateFormat(val, "yyyy-mm-dd");
@@ -821,7 +821,7 @@ If you don't configure a logging adapter, the default is LogToEmail, but the def
Use this method in place of `representationOf()` to return a stream of binary data. Useful for streaming things like dynamically generated PDFs. **Note: ** When streaming binary data as the response, you must set the mime type manually using [withMime()](#withmime). For example:
```js
```cfscript
return streamBinary(local.pdf).withStatus(200).withMime("application/pdf");
```
@@ -834,7 +834,7 @@ return streamBinary(local.pdf).withStatus(200).withMime("application/pdf");
Use this method in place of `representationOf()` to stream a file from disk (or VFS). Optionally append `.andDelete( true )` to delete the file once streaming is complete. **Note: ** When streaming binary data as the response, you must set the mime type manually using [withMime()](#withmime). For example:
```js
```cfscript
return streamFile("/foo.txt")
.andDelete(true)
.withStatus(200)
@@ -859,7 +859,7 @@ Use this method in place of `representationOf()` to stream an image from disk (o
This special method _**requires**_ the use of either **noData** or **representationOf**. It adds custom headers to the return. Additional use of **withStatus** optional.
```js
```cfscript
return representationOf(myData).withHeaders({"X-POWERED-BY"="Taffy 2.0!"});
```
@@ -872,7 +872,7 @@ return representationOf(myData).withHeaders({"X-POWERED-BY"="Taffy 2.0!"});
This special method _**requires**_ the use of either **streamFile** or **streamBinary**. It overrides the default mime type header for the return.
```js
```cfscript
return streamFile("kittens/cuteness.pdf").withMime("application/pdf");
```
@@ -888,7 +888,7 @@ This special method _**requires**_ the use of either **noData** or **representat
_If you do not specify a return status code, Taffy will always return status code 200 (OK) by default._
```js
```cfscript
return noData().withStatus(404, "Not Found");
```
@@ -1088,7 +1088,7 @@ A new argument has been appended to onTaffyRequest, where Taffy will pass the or
This makes the latest onTaffyRequest method signature:
```js
```cfscript
function onTaffyRequest(
verb,
cfc,
+30 -30
View File
@@ -12,7 +12,7 @@ The REST Web Service framework for ColdFusion and Lucee
**Application.cfc:**
```js
```cfscript
component extends="taffy.core.api" {}
```
@@ -24,7 +24,7 @@ component extends="taffy.core.api" {}
**/resources/hello.cfc:**
```js
```cfscript
component extends="taffy.core.resource" taffy_uri="/hello" {
function get(){
@@ -77,13 +77,13 @@ You can see that the taffy folder is a sibling to Application.cfc. This allows A
Next, if your Application.cfc and `/resources/` folder aren't in the web-root (e.g. they're inside something like `/api/`) then you'll need to add an [application-specific mapping](http://livedocs.adobe.com/coldfusion/8/htmldocs/help.html?content=appFramework_04.html) for `/resources` so that Taffy can find your resources to initialize the routes.
```js
```cfscript
this.mappings["/resources"] = expandPath("./resources");
```
You'll also need to add a mapping for `/taffy` so that the resources can extend `taffy.core.resource` (since the taffy folder isn't a child of the resources folder):
```js
```cfscript
this.mappings["/taffy"] = expandPath("./taffy");
```
@@ -132,7 +132,7 @@ In the xml above you can see that I only have 1 wildcard, but to compensate I've
Taffy allows for setting the HTTP status message using [.withStatus()](#withstatus), such as:
```js
```cfscript
return representationOf({...}).withStatus(403, "Not Authorized");
```
@@ -221,7 +221,7 @@ The **taffy:uri** property applies to the `<cfcomponent>` tag or the `component{
or
```js
```cfscript
component taffy_uri="/artist/{artistId}" {
}
```
@@ -252,7 +252,7 @@ By convention, resources will automatically map the 4 primary HTTP REST verbs --
or
```js
```cfscript
function getUser( numeric userId ) taffy_verb="get" {
}
```
@@ -272,7 +272,7 @@ By convention, the mime-types supported by your API are determined by the method
or
```js
```cfscript
function getAsJson() taffy_mime="application/json" {
}
```
@@ -291,7 +291,7 @@ When your API supports more than one data format (i.e. json and xml), you must s
or
```js
```cfscript
function getAsJson() taffy_mime="application/json" taffy_default="true" {}
function getAsXml() taffy_mime="application/xml" {}
@@ -301,7 +301,7 @@ function getAsXml() taffy_mime="application/xml" {}
Default values:
```js
```cfscript
variables.framework = {
reloadKey = "reload",
reloadPassword = "true",
@@ -462,7 +462,7 @@ The allowed verbs, of course, are the ones allowed by the requested resource, as
In addition, as of Taffy 3.1.0, you can set this setting to a string of allowable hosts, as a (comma, semicolon, or space) delimited list:
```js
```cfscript
variables.framework.allowCrossDomain =
"http://example.com; http://foo.bar, http://google.com";
```
@@ -522,7 +522,7 @@ Global headers are static. You set them on application initialization and they d
**NOTE FOR EXTERNAL BEAN FACTORY USERS (e.g. Coldspring, DI/1 etc)** If your external bean factory is initialized in onApplicationStart then you need to set the variables.framework.beanFactory after your bean factory has initialized, for example:
```js
```cfscript
component extends="taffy.core.api"
{
this.name = 'myapi';
@@ -584,7 +584,7 @@ Taffy calls this method during initialization to determine in which configured e
The returned value will be used to load environment-specific configuration. For example, if you have the following code in your Application.cfc, then the dashboard will be disabled in production:
```js
```cfscript
variables.framework = {
disableDashboard = false
@@ -635,13 +635,13 @@ This method is optional, and allows you to inspect and potentially abort an API
If you do not return TRUE, allowing the request to continue as normal, then Taffy expects you to call and return the result of either **[noData()](#nodata)** or **[representationOf()](#representationof)**. If you simply want to return with a status code of 403 (which indicates "Not Allowed") and no response body, you could do this:
```js
```cfscript
return noData().withStatus(403);
```
Alternately, you could return some data to indicate that they owe you money or something:
```js
```cfscript
return representationOf({
error="Your account is past due. Please email accounts payable."
})
@@ -652,7 +652,7 @@ The options here are limited only by your imagination.
You can add data to the **requestArguments** structure and this will be passed on to any resource that handles the request. Simply add a key to the structure:
```js
```cfscript
function onTaffyRequest(
verb,
cfc,
@@ -669,7 +669,7 @@ function onTaffyRequest(
In your resource:
```js
```cfscript
function get(myData) {
//arguments.myData => "myValue"
}
@@ -677,13 +677,13 @@ function get(myData) {
You can use the method metadata for anything you see fit; but the original use case was for role-based security. Consider the following resource method:
```js
```cfscript
public function getData( id ) taffy_method="get" role="datareader" { ... }
```
Taffy doesn't do anything with the **role** metadata on this method other than expose it to you in onTaffyRequest. So let's use the user's API key to find out what roles they have, and verify that the method's required role is among them. This is a snippet from your Application.cfc:
```js
```cfscript
function onTaffyRequest(verb, cfc, requestArgs, mime, head, methodMetadata){
local.user = (...); //get user from api key...
@@ -768,7 +768,7 @@ Resource CFCs extend `taffy.core.resource`. The following methods are available
This method allows you to specify that there is no data to be returned for the current request. Generally, you would use it in conjunction with the **withStatus** method to set a specific return status for the request. For example, if the requested resource doesn't exist, you could return a 404 error like so:
```js
```cfscript
return noData().withStatus(404);
```
@@ -784,7 +784,7 @@ This method transforms a ColdFusion query object into an array of structures. It
The callback function can be used to efficiently transform keys in the structure before it is added to the array without additional looping. For example, you can use it to format dates in a particular style. **Your callback must return a value.**
```js
```cfscript
queryToArray(someQ, function (row) {
row.startDate = dateFormat(row.startDate, "yyyy-mm-dd");
return row;
@@ -793,7 +793,7 @@ queryToArray(someQ, function (row) {
When you want to return the resulting array as your API response, you must still wrap it in a [representationOf](#rep-1) call:
```js
```cfscript
return rep(queryToArray(someQ));
```
@@ -811,7 +811,7 @@ This method transforms a ColdFusion query object into a structure. If there is m
The callback function is passed the column name and the value, and can be used to efficiently transform keys in the structure as they are read from the query without need for additional looping. For example, you can use it to format dates in a particular style. **Your callback must return a value.**
```js
```cfscript
return queryToStruct(someQ, function (colName, val) {
if (colName == "startDate") {
return dateFormat(val, "yyyy-mm-dd");
@@ -859,7 +859,7 @@ If you don't configure a logging adapter, the default is LogToEmail, but the def
Use this method in place of `representationOf()` to return a stream of binary data. Useful for streaming things like dynamically generated PDFs. **Note: ** When streaming binary data as the response, you must set the mime type manually using [withMime()](#withmime). For example:
```js
```cfscript
return streamBinary(local.pdf).withStatus(200).withMime("application/pdf");
```
@@ -872,7 +872,7 @@ return streamBinary(local.pdf).withStatus(200).withMime("application/pdf");
Use this method in place of `representationOf()` to stream a file from disk (or VFS). Optionally append `.andDelete( true )` to delete the file once streaming is complete. **Note: ** When streaming binary data as the response, you must set the mime type manually using [withMime()](#withmime). For example:
```js
```cfscript
return streamFile("/foo.txt")
.andDelete(true)
.withStatus(200)
@@ -897,7 +897,7 @@ Use this method in place of `representationOf()` to stream an image from disk (o
This special method _**requires**_ the use of either **noData** or **representationOf**. It adds custom headers to the return. Additional use of **withStatus** optional.
```js
```cfscript
return representationOf(myData).withHeaders({"X-POWERED-BY"="Taffy 2.0!"});
```
@@ -910,7 +910,7 @@ return representationOf(myData).withHeaders({"X-POWERED-BY"="Taffy 2.0!"});
This special method _**requires**_ the use of either **streamFile** or **streamBinary**. It overrides the default mime type header for the return.
```js
```cfscript
return streamFile("kittens/cuteness.pdf").withMime("application/pdf");
```
@@ -926,7 +926,7 @@ This special method _**requires**_ the use of either **noData** or **representat
_If you do not specify a return status code, Taffy will always return status code 200 (OK) by default._
```js
```cfscript
return noData().withStatus(404, "Not Found");
```
@@ -1116,7 +1116,7 @@ And thanks to [a pull request](https://github.com/atuttle/pull/256) from @dskagg
Previously, the LogToEmail adapter supported only **emailFrom, emailTo, emailSubj,** and **emailType** config attributes, which were used to send the email. Now all cfmail attributes are supported (and properly named: from, to, subject, type, server, username, password, etc &mdash; and still backwards compatible with emailSubj, emailTo, emailFrom, and emailType). Here's an example [framework.exceptionLogAdapterConfig](#exceptionlogadapterconfig):
```js
```cfscript
variables.framework.exceptionLogAdapter = "taffy.bonus.LogToEmail";
variables.framework.exceptionLogAdapterConfig = {
to: "errors@example.com",
@@ -1166,7 +1166,7 @@ GET /translations?language[]=en&language[]=fr&language[]=de
jQuery in particular (among others), will send data in this format, given the following input:
```js
```cfscript
$.ajax({
type: "GET",
url: "translations",
+32 -32
View File
@@ -12,7 +12,7 @@ The REST Web Service framework for ColdFusion and Lucee
**Application.cfc:**
```js
```cfscript
component extends="taffy.core.api" {}
```
@@ -24,7 +24,7 @@ component extends="taffy.core.api" {}
**/resources/hello.cfc:**
```js
```cfscript
component extends="taffy.core.resource" taffy_uri="/hello" {
function get(){
@@ -77,13 +77,13 @@ You can see that the taffy folder is a sibling to Application.cfc. This allows A
Next, if your Application.cfc and `/resources/` folder aren't in the web-root (e.g. they're inside something like `/api/`) then you'll need to add an [application-specific mapping](http://livedocs.adobe.com/coldfusion/8/htmldocs/help.html?content=appFramework_04.html) for `/resources` so that Taffy can find your resources to initialize the routes.
```js
```cfscript
this.mappings["/resources"] = expandPath("./resources");
```
You'll also need to add a mapping for `/taffy` so that the resources can extend `taffy.core.resource` (since the taffy folder isn't a child of the resources folder):
```js
```cfscript
this.mappings["/taffy"] = expandPath("./taffy");
```
@@ -132,7 +132,7 @@ In the xml above you can see that I only have 1 wildcard, but to compensate I've
Taffy allows for setting the HTTP status message using [.withStatus()](#withstatus), such as:
```js
```cfscript
return representationOf({...}).withStatus(403, "Not Authorized");
```
@@ -221,7 +221,7 @@ If you want a resource not to be included in the dashboard, set `taffy:dashboard
or
```js
```cfscript
component taffy_uri="/secret-squirrel" taffy_dashboard_hide {
}
```
@@ -251,7 +251,7 @@ The **taffy:uri** property applies to the `<cfcomponent>` tag or the `component{
or
```js
```cfscript
component taffy_uri="/artist/{artistId}" {
}
```
@@ -282,7 +282,7 @@ By convention, resources will automatically map the 4 primary HTTP REST verbs --
or
```js
```cfscript
function getUser( numeric userId ) taffy_verb="get" {
}
```
@@ -298,7 +298,7 @@ You can prevent a resource from showing in the documentation by adding the `taff
or
```js
```cfscript
component extends="taffy.core.resource" taffy_uri="/artist/{artistId}" taffy_docs_hide {
}
```
@@ -314,7 +314,7 @@ Similarly, you can hide methods and parameters by adding the attribute to the `<
or
```js
```cfscript
public function getUser(
required numeric userId,
string _hidden = "" taffy_docs_hide
@@ -339,7 +339,7 @@ By convention, the mime-types supported by your API are determined by the method
or
```js
```cfscript
function getAsJson() taffy_mime="application/json" {
}
```
@@ -358,7 +358,7 @@ When your API supports more than one data format (i.e. json and xml), you must s
or
```js
```cfscript
function getAsJson() taffy_mime="application/json" taffy_default="true" {}
function getAsXml() taffy_mime="application/xml" {}
@@ -368,7 +368,7 @@ function getAsXml() taffy_mime="application/xml" {}
Default values:
```js
```cfscript
variables.framework = {
reloadKey = "reload",
reloadPassword = "true",
@@ -529,7 +529,7 @@ The allowed verbs, of course, are the ones allowed by the requested resource, as
In addition, as of Taffy 3.1.0, you can set this setting to a string of allowable hosts, as a (comma, semicolon, or space) delimited list:
```js
```cfscript
variables.framework.allowCrossDomain =
"http://example.com; http://foo.bar, http://google.com";
```
@@ -589,7 +589,7 @@ Global headers are static. You set them on application initialization and they d
**NOTE FOR EXTERNAL BEAN FACTORY USERS (e.g. Coldspring, DI/1 etc)** If your external bean factory is initialized in onApplicationStart then you need to set the variables.framework.beanFactory after your bean factory has initialized, for example:
```js
```cfscript
component extends="taffy.core.api"
{
this.name = 'myapi';
@@ -651,7 +651,7 @@ Taffy calls this method during initialization to determine in which configured e
The returned value will be used to load environment-specific configuration. For example, if you have the following code in your Application.cfc, then the dashboard will be disabled in production:
```js
```cfscript
variables.framework = {
disableDashboard = false
@@ -702,13 +702,13 @@ This method is optional, and allows you to inspect and potentially abort an API
If you do not return TRUE, allowing the request to continue as normal, then Taffy expects you to call and return the result of either **[noData()](#nodata)** or **[representationOf()](#representationof)**. If you simply want to return with a status code of 403 (which indicates "Not Allowed") and no response body, you could do this:
```js
```cfscript
return noData().withStatus(403);
```
Alternately, you could return some data to indicate that they owe you money or something:
```js
```cfscript
return representationOf({
error="Your account is past due. Please email accounts payable."
})
@@ -719,7 +719,7 @@ The options here are limited only by your imagination.
You can add data to the **requestArguments** structure and this will be passed on to any resource that handles the request. Simply add a key to the structure:
```js
```cfscript
function onTaffyRequest(
verb,
cfc,
@@ -736,7 +736,7 @@ function onTaffyRequest(
In your resource:
```js
```cfscript
function get(myData) {
//arguments.myData => "myValue"
}
@@ -744,13 +744,13 @@ function get(myData) {
You can use the method metadata for anything you see fit; but the original use case was for role-based security. Consider the following resource method:
```js
```cfscript
public function getData( id ) taffy_method="get" role="datareader" { ... }
```
Taffy doesn't do anything with the **role** metadata on this method other than expose it to you in onTaffyRequest. So let's use the user's API key to find out what roles they have, and verify that the method's required role is among them. This is a snippet from your Application.cfc:
```js
```cfscript
function onTaffyRequest(verb, cfc, requestArgs, mime, head, methodMetadata){
local.user = (...); //get user from api key...
@@ -837,7 +837,7 @@ Resource CFCs extend `taffy.core.resource`. The following methods are available
This is a convenience method used to make sure that certain all-numeric inputs get serialized as a string in the output not converted to numeric output. Examples are postal codes or phone numbers.
```js
```cfscript
return rep(
queryToArray(myQuery, function (row) {
row.phone = encode.string(row.phone);
@@ -853,7 +853,7 @@ return rep(
This method allows you to specify that there is no data to be returned for the current request. Generally, you would use it in conjunction with the **withStatus** method to set a specific return status for the request. For example, if the requested resource doesn't exist, you could return a 404 error like so:
```js
```cfscript
return noData().withStatus(404);
```
@@ -869,7 +869,7 @@ This method transforms a ColdFusion query object into an array of structures. It
The callback function can be used to efficiently transform keys in the structure before it is added to the array without additional looping. For example, you can use it to format dates in a particular style. **Your callback must return a value.**
```js
```cfscript
queryToArray(someQ, function (row) {
row.startDate = dateFormat(row.startDate, "yyyy-mm-dd");
return row;
@@ -878,7 +878,7 @@ queryToArray(someQ, function (row) {
When you want to return the resulting array as your API response, you must still wrap it in a [representationOf](#rep-1) call:
```js
```cfscript
return rep(queryToArray(someQ));
```
@@ -896,7 +896,7 @@ This method transforms a ColdFusion query object into a structure. If there is m
The callback function is passed the column name and the value, and can be used to efficiently transform keys in the structure as they are read from the query without need for additional looping. For example, you can use it to format dates in a particular style. **Your callback must return a value.**
```js
```cfscript
return queryToStruct(someQ, function (colName, val) {
if (colName == "startDate") {
return dateFormat(val, "yyyy-mm-dd");
@@ -944,7 +944,7 @@ If you don't configure a logging adapter, the default is LogToEmail, but the def
Use this method in place of `representationOf()` to return a stream of binary data. Useful for streaming things like dynamically generated PDFs. **Note: ** When streaming binary data as the response, you must set the mime type manually using [withMime()](#withmime). For example:
```js
```cfscript
return streamBinary(local.pdf).withStatus(200).withMime("application/pdf");
```
@@ -957,7 +957,7 @@ return streamBinary(local.pdf).withStatus(200).withMime("application/pdf");
Use this method in place of `representationOf()` to stream a file from disk (or VFS). Optionally append `.andDelete( true )` to delete the file once streaming is complete. **Note: ** When streaming binary data as the response, you must set the mime type manually using [withMime()](#withmime). For example:
```js
```cfscript
return streamFile("/foo.txt")
.andDelete(true)
.withStatus(200)
@@ -982,7 +982,7 @@ Use this method in place of `representationOf()` to stream an image from disk (o
This special method _**requires**_ the use of either **noData** or **representationOf**. It adds custom headers to the return. Additional use of **withStatus** optional.
```js
```cfscript
return representationOf(myData).withHeaders({"X-POWERED-BY"="Taffy 2.0!"});
```
@@ -995,7 +995,7 @@ return representationOf(myData).withHeaders({"X-POWERED-BY"="Taffy 2.0!"});
This special method _**requires**_ the use of either **streamFile** or **streamBinary**. It overrides the default mime type header for the return.
```js
```cfscript
return streamFile("kittens/cuteness.pdf").withMime("application/pdf");
```
@@ -1011,7 +1011,7 @@ This special method _**requires**_ the use of either **noData** or **representat
_If you do not specify a return status code, Taffy will always return status code 200 (OK) by default._
```js
```cfscript
return noData().withStatus(404, "Not Found");
```
+32 -32
View File
@@ -12,7 +12,7 @@ The REST Web Service framework for ColdFusion and Lucee
**Application.cfc:**
```js
```cfscript
component extends="taffy.core.api" {}
```
@@ -24,7 +24,7 @@ component extends="taffy.core.api" {}
**/resources/hello.cfc:**
```js
```cfscript
component extends="taffy.core.resource" taffy_uri="/hello" {
function get(){
@@ -77,13 +77,13 @@ You can see that the taffy folder is a sibling to Application.cfc. This allows A
Next, if your Application.cfc and `/resources/` folder aren't in the web-root (e.g. they're inside something like `/api/`) then you'll need to add an [application-specific mapping](http://livedocs.adobe.com/coldfusion/8/htmldocs/help.html?content=appFramework_04.html) for `/resources` so that Taffy can find your resources to initialize the routes.
```js
```cfscript
this.mappings["/resources"] = expandPath("./resources");
```
You'll also need to add a mapping for `/taffy` so that the resources can extend `taffy.core.resource` (since the taffy folder isn't a child of the resources folder):
```js
```cfscript
this.mappings["/taffy"] = expandPath("./taffy");
```
@@ -132,7 +132,7 @@ In the xml above you can see that I only have 1 wildcard, but to compensate I've
Taffy allows for setting the HTTP status message using [.withStatus()](#withstatus), such as:
```js
```cfscript
return representationOf({...}).withStatus(403, "Not Authorized");
```
@@ -221,7 +221,7 @@ If you want a resource not to be included in the dashboard, set `taffy:dashboard
or
```js
```cfscript
component taffy_uri="/secret-squirrel" taffy_dashboard_hide {
}
```
@@ -247,7 +247,7 @@ The **taffy:uri** property applies to the `<cfcomponent>` tag or the `component{
or
```js
```cfscript
component taffy_uri="/artist/{artistId}" {
}
```
@@ -278,7 +278,7 @@ By convention, resources will automatically map the 4 primary HTTP REST verbs --
or
```js
```cfscript
function getUser( numeric userId ) taffy_verb="get" {
}
```
@@ -294,7 +294,7 @@ You can prevent a resource from showing in the documentation by adding the `taff
or
```js
```cfscript
component extends="taffy.core.resource" taffy_uri="/artist/{artistId}" taffy_docs_hide {
}
```
@@ -310,7 +310,7 @@ Similarly, you can hide methods and parameters by adding the attribute to the `<
or
```js
```cfscript
public function getUser(
required numeric userId,
string _hidden = "" taffy_docs_hide
@@ -335,7 +335,7 @@ By convention, the mime-types supported by your API are determined by the method
or
```js
```cfscript
function getAsJson() taffy_mime="application/json" {
}
```
@@ -354,7 +354,7 @@ When your API supports more than one data format (i.e. json and xml), you must s
or
```js
```cfscript
function getAsJson() taffy_mime="application/json" taffy_default="true" {}
function getAsXml() taffy_mime="application/xml" {}
@@ -364,7 +364,7 @@ function getAsXml() taffy_mime="application/xml" {}
Default values:
```js
```cfscript
variables.framework = {
reloadKey = "reload",
reloadPassword = "true",
@@ -570,7 +570,7 @@ The allowed verbs, of course, are the ones allowed by the requested resource, as
In addition, as of Taffy 3.1.0, you can set this setting to a string of allowable hosts, as a (comma, semicolon, or space) delimited list:
```js
```cfscript
variables.framework.allowCrossDomain =
"http://example.com; http://foo.bar, http://google.com";
```
@@ -637,7 +637,7 @@ Global headers are static. You set them on application initialization and they d
**NOTE FOR EXTERNAL BEAN FACTORY USERS (e.g. Coldspring, DI/1 etc)** If your external bean factory is initialized in onApplicationStart then you need to set the variables.framework.beanFactory after your bean factory has initialized, for example:
```js
```cfscript
component extends="taffy.core.api"
{
this.name = 'myapi';
@@ -710,7 +710,7 @@ Taffy calls this method during initialization to determine in which configured e
The returned value will be used to load environment-specific configuration. For example, if you have the following code in your Application.cfc, then the dashboard will be disabled in production:
```js
```cfscript
variables.framework = {
disableDashboard = false
@@ -769,13 +769,13 @@ This method is optional, and allows you to inspect and potentially abort an API
If you do not return TRUE, allowing the request to continue as normal, then Taffy expects you to call and return the result of either **[noData()](#nodata)** or **[representationOf()](#representationof)**. If you simply want to return with a status code of 403 (which indicates "Not Allowed") and no response body, you could do this:
```js
```cfscript
return noData().withStatus(403);
```
Alternately, you could return some data to indicate that they owe you money or something:
```js
```cfscript
return representationOf({
error="Your account is past due. Please email accounts payable."
})
@@ -786,7 +786,7 @@ The options here are limited only by your imagination.
You can add data to the **requestArguments** structure and this will be passed on to any resource that handles the request. Simply add a key to the structure:
```js
```cfscript
function onTaffyRequest(
verb,
cfc,
@@ -803,7 +803,7 @@ function onTaffyRequest(
In your resource:
```js
```cfscript
function get(myData) {
//arguments.myData => "myValue"
}
@@ -811,13 +811,13 @@ function get(myData) {
You can use the method metadata for anything you see fit; but the original use case was for role-based security. Consider the following resource method:
```js
```cfscript
public function getData( id ) taffy_method="get" role="datareader" { ... }
```
Taffy doesn't do anything with the **role** metadata on this method other than expose it to you in onTaffyRequest. So let's use the user's API key to find out what roles they have, and verify that the method's required role is among them. This is a snippet from your Application.cfc:
```js
```cfscript
function onTaffyRequest(verb, cfc, requestArgs, mime, head, methodMetadata){
local.user = (...); //get user from api key...
@@ -905,7 +905,7 @@ Resource CFCs extend `taffy.core.resource`. The following methods are available
This is a convenience method used to make sure that certain all-numeric inputs get serialized as a string in the output not converted to numeric output. Examples are postal codes or phone numbers.
```js
```cfscript
return rep(
queryToArray(myQuery, function (row) {
row.phone = encode.string(row.phone);
@@ -929,7 +929,7 @@ Behaves like `noData()` except that it sets the status code to 204 and the Conte
This method allows you to specify that there is no data to be returned for the current request. Generally, you would use it in conjunction with the **withStatus** method to set a specific return status for the request. For example, if the requested resource doesn't exist, you could return a 404 error like so:
```js
```cfscript
return noData().withStatus(404);
```
@@ -947,7 +947,7 @@ This method transforms a ColdFusion query object into an array of structures. It
The callback function can be used to efficiently transform keys in the structure before it is added to the array without additional looping. For example, you can use it to format dates in a particular style. **Your callback must return a value.**
```js
```cfscript
queryToArray(someQ, function (row) {
row.startDate = dateFormat(row.startDate, "yyyy-mm-dd");
return row;
@@ -956,7 +956,7 @@ queryToArray(someQ, function (row) {
When you want to return the resulting array as your API response, you must still wrap it in a [representationOf](#rep-1) call:
```js
```cfscript
return rep(queryToArray(someQ));
```
@@ -974,7 +974,7 @@ This method transforms a ColdFusion query object into a structure. If there is m
The callback function is passed the column name and the value, and can be used to efficiently transform keys in the structure as they are read from the query without need for additional looping. For example, you can use it to format dates in a particular style. **Your callback must return a value.**
```js
```cfscript
return queryToStruct(someQ, function (colName, val) {
if (colName == "startDate") {
return dateFormat(val, "yyyy-mm-dd");
@@ -1022,7 +1022,7 @@ If you don't configure a logging adapter, the default is LogToEmail, but the def
Use this method in place of `representationOf()` to return a stream of binary data. Useful for streaming things like dynamically generated PDFs. **Note: ** When streaming binary data as the response, you must set the mime type manually using [withMime()](#withmime). For example:
```js
```cfscript
return streamBinary(local.pdf).withStatus(200).withMime("application/pdf");
```
@@ -1035,7 +1035,7 @@ return streamBinary(local.pdf).withStatus(200).withMime("application/pdf");
Use this method in place of `representationOf()` to stream a file from disk (or VFS). Optionally append `.andDelete( true )` to delete the file once streaming is complete. **Note: ** When streaming binary data as the response, you must set the mime type manually using [withMime()](#withmime). For example:
```js
```cfscript
return streamFile("/foo.txt")
.andDelete(true)
.withStatus(200)
@@ -1060,7 +1060,7 @@ Use this method in place of `representationOf()` to stream an image from disk (o
This special method _**requires**_ the use of either **noData** or **representationOf**. It adds custom headers to the return. Additional use of **withStatus** optional.
```js
```cfscript
return representationOf(myData).withHeaders({"X-POWERED-BY"="Taffy 2.0!"});
```
@@ -1073,7 +1073,7 @@ return representationOf(myData).withHeaders({"X-POWERED-BY"="Taffy 2.0!"});
This special method _**requires**_ the use of either **streamFile** or **streamBinary**. It overrides the default mime type header for the return.
```js
```cfscript
return streamFile("kittens/cuteness.pdf").withMime("application/pdf");
```
@@ -1089,7 +1089,7 @@ This special method _**requires**_ the use of either **noData** or **representat
_If you do not specify a return status code, Taffy will always return status code 200 (OK) by default._
```js
```cfscript
return noData().withStatus(404, "Not Found");
```
+32 -32
View File
@@ -12,7 +12,7 @@ The REST Web Service framework for ColdFusion and Lucee
**Application.cfc:**
```js
```cfscript
component extends="taffy.core.api" {}
```
@@ -24,7 +24,7 @@ component extends="taffy.core.api" {}
**/resources/hello.cfc:**
```js
```cfscript
component extends="taffy.core.resource" taffy_uri="/hello" {
function get(){
@@ -77,13 +77,13 @@ You can see that the taffy folder is a sibling to Application.cfc. This allows A
Next, if your Application.cfc and `/resources/` folder aren't in the web-root (e.g. they're inside something like `/api/`) then you'll need to add an [application-specific mapping](http://livedocs.adobe.com/coldfusion/8/htmldocs/help.html?content=appFramework_04.html) for `/resources` so that Taffy can find your resources to initialize the routes.
```js
```cfscript
this.mappings["/resources"] = expandPath("./resources");
```
You'll also need to add a mapping for `/taffy` so that the resources can extend `taffy.core.resource` (since the taffy folder isn't a child of the resources folder):
```js
```cfscript
this.mappings["/taffy"] = expandPath("./taffy");
```
@@ -132,7 +132,7 @@ In the xml above you can see that I only have 1 wildcard, but to compensate I've
Taffy allows for setting the HTTP status message using [.withStatus()](#withstatus), such as:
```js
```cfscript
return representationOf({...}).withStatus(403, "Not Authorized");
```
@@ -221,7 +221,7 @@ If you want a resource not to be included in the dashboard, set `taffy:dashboard
or
```js
```cfscript
component taffy_uri="/secret-squirrel" taffy_dashboard_hide {
}
```
@@ -247,7 +247,7 @@ The **taffy:uri** property applies to the `<cfcomponent>` tag or the `component{
or
```js
```cfscript
component taffy_uri="/artist/{artistId}" {
}
```
@@ -278,7 +278,7 @@ By convention, resources will automatically map the 4 primary HTTP REST verbs --
or
```js
```cfscript
function getUser( numeric userId ) taffy_verb="get" {
}
```
@@ -294,7 +294,7 @@ You can prevent a resource from showing in the documentation by adding the `taff
or
```js
```cfscript
component extends="taffy.core.resource" taffy_uri="/artist/{artistId}" taffy_docs_hide {
}
```
@@ -310,7 +310,7 @@ Similarly, you can hide methods and parameters by adding the attribute to the `<
or
```js
```cfscript
public function getUser(
required numeric userId,
string _hidden = "" taffy_docs_hide
@@ -335,7 +335,7 @@ By convention, the mime-types supported by your API are determined by the method
or
```js
```cfscript
function getAsJson() taffy_mime="application/json" {
}
```
@@ -354,7 +354,7 @@ When your API supports more than one data format (i.e. json and xml), you must s
or
```js
```cfscript
function getAsJson() taffy_mime="application/json" taffy_default="true" {}
function getAsXml() taffy_mime="application/xml" {}
@@ -364,7 +364,7 @@ function getAsXml() taffy_mime="application/xml" {}
Default values:
```js
```cfscript
variables.framework = {
reloadKey = "reload",
reloadPassword = "true",
@@ -570,7 +570,7 @@ The allowed verbs, of course, are the ones allowed by the requested resource, as
In addition, as of Taffy 3.1.0, you can set this setting to a string of allowable hosts, as a (comma, semicolon, or space) delimited list:
```js
```cfscript
variables.framework.allowCrossDomain =
"http://example.com; http://foo.bar, http://google.com";
```
@@ -637,7 +637,7 @@ Global headers are static. You set them on application initialization and they d
**NOTE FOR EXTERNAL BEAN FACTORY USERS (e.g. Coldspring, DI/1 etc)** If your external bean factory is initialized in onApplicationStart then you need to set the variables.framework.beanFactory after your bean factory has initialized, for example:
```js
```cfscript
component extends="taffy.core.api"
{
this.name = 'myapi';
@@ -710,7 +710,7 @@ Taffy calls this method during initialization to determine in which configured e
The returned value will be used to load environment-specific configuration. For example, if you have the following code in your Application.cfc, then the dashboard will be disabled in production:
```js
```cfscript
variables.framework = {
disableDashboard = false
@@ -769,13 +769,13 @@ This method is optional, and allows you to inspect and potentially abort an API
If you do not return TRUE, allowing the request to continue as normal, then Taffy expects you to call and return the result of either **[noData()](#nodata)** or **[representationOf()](#representationof)**. If you simply want to return with a status code of 403 (which indicates "Not Allowed") and no response body, you could do this:
```js
```cfscript
return noData().withStatus(403);
```
Alternately, you could return some data to indicate that they owe you money or something:
```js
```cfscript
return representationOf({
error="Your account is past due. Please email accounts payable."
})
@@ -786,7 +786,7 @@ The options here are limited only by your imagination.
You can add data to the **requestArguments** structure and this will be passed on to any resource that handles the request. Simply add a key to the structure:
```js
```cfscript
function onTaffyRequest(
verb,
cfc,
@@ -803,7 +803,7 @@ function onTaffyRequest(
In your resource:
```js
```cfscript
function get(myData) {
//arguments.myData => "myValue"
}
@@ -811,13 +811,13 @@ function get(myData) {
You can use the method metadata for anything you see fit; but the original use case was for role-based security. Consider the following resource method:
```js
```cfscript
public function getData( id ) taffy_method="get" role="datareader" { ... }
```
Taffy doesn't do anything with the **role** metadata on this method other than expose it to you in onTaffyRequest. So let's use the user's API key to find out what roles they have, and verify that the method's required role is among them. This is a snippet from your Application.cfc:
```js
```cfscript
function onTaffyRequest(verb, cfc, requestArgs, mime, head, methodMetadata){
local.user = (...); //get user from api key...
@@ -914,7 +914,7 @@ This method saves data in a way that makes it available to [exception log adapte
This is a convenience method used to make sure that certain all-numeric inputs get serialized as a string in the output not converted to numeric output. Examples are postal codes or phone numbers.
```js
```cfscript
return rep(
queryToArray(myQuery, function (row) {
row.phone = encode.string(row.phone);
@@ -938,7 +938,7 @@ Behaves like `noData()` except that it sets the status code to 204 and the Conte
This method allows you to specify that there is no data to be returned for the current request. Generally, you would use it in conjunction with the **withStatus** method to set a specific return status for the request. For example, if the requested resource doesn't exist, you could return a 404 error like so:
```js
```cfscript
return noData().withStatus(404);
```
@@ -956,7 +956,7 @@ This method transforms a ColdFusion query object into an array of structures. It
The callback function can be used to efficiently transform keys in the structure before it is added to the array without additional looping. For example, you can use it to format dates in a particular style. **Your callback must return a value.**
```js
```cfscript
queryToArray(someQ, function (row) {
row.startDate = dateFormat(row.startDate, "yyyy-mm-dd");
return row;
@@ -965,7 +965,7 @@ queryToArray(someQ, function (row) {
When you want to return the resulting array as your API response, you must still wrap it in a [representationOf](#rep-1) call:
```js
```cfscript
return rep(queryToArray(someQ));
```
@@ -983,7 +983,7 @@ This method transforms a ColdFusion query object into a structure. If there is m
The callback function is passed the column name and the value, and can be used to efficiently transform keys in the structure as they are read from the query without need for additional looping. For example, you can use it to format dates in a particular style. **Your callback must return a value.**
```js
```cfscript
return queryToStruct(someQ, function (colName, val) {
if (colName == "startDate") {
return dateFormat(val, "yyyy-mm-dd");
@@ -1031,7 +1031,7 @@ If you don't configure a logging adapter, the default is LogToEmail, but the def
Use this method in place of `representationOf()` to return a stream of binary data. Useful for streaming things like dynamically generated PDFs. **Note: ** When streaming binary data as the response, you must set the mime type manually using [withMime()](#withmime). For example:
```js
```cfscript
return streamBinary(local.pdf).withStatus(200).withMime("application/pdf");
```
@@ -1044,7 +1044,7 @@ return streamBinary(local.pdf).withStatus(200).withMime("application/pdf");
Use this method in place of `representationOf()` to stream a file from disk (or VFS). Optionally append `.andDelete( true )` to delete the file once streaming is complete. **Note: ** When streaming binary data as the response, you must set the mime type manually using [withMime()](#withmime). For example:
```js
```cfscript
return streamFile("/foo.txt")
.andDelete(true)
.withStatus(200)
@@ -1069,7 +1069,7 @@ Use this method in place of `representationOf()` to stream an image from disk (o
This special method _**requires**_ the use of either **noData** or **representationOf**. It adds custom headers to the return. Additional use of **withStatus** optional.
```js
```cfscript
return representationOf(myData).withHeaders({"X-POWERED-BY"="Taffy 2.0!"});
```
@@ -1082,7 +1082,7 @@ return representationOf(myData).withHeaders({"X-POWERED-BY"="Taffy 2.0!"});
This special method _**requires**_ the use of either **streamFile** or **streamBinary**. It overrides the default mime type header for the return.
```js
```cfscript
return streamFile("kittens/cuteness.pdf").withMime("application/pdf");
```
@@ -1098,7 +1098,7 @@ This special method _**requires**_ the use of either **noData** or **representat
_If you do not specify a return status code, Taffy will always return status code 200 (OK) by default._
```js
```cfscript
return noData().withStatus(404, "Not Found");
```
+32 -32
View File
@@ -12,7 +12,7 @@ The REST Web Service framework for ColdFusion and Lucee
**Application.cfc:**
```js
```cfscript
component extends="taffy.core.api" {}
```
@@ -24,7 +24,7 @@ component extends="taffy.core.api" {}
**/resources/hello.cfc:**
```js
```cfscript
component extends="taffy.core.resource" taffy_uri="/hello" {
function get(){
@@ -77,13 +77,13 @@ You can see that the taffy folder is a sibling to Application.cfc. This allows A
Next, if your Application.cfc and `/resources/` folder aren't in the web-root (e.g. they're inside something like `/api/`) then you'll need to add an [application-specific mapping](http://livedocs.adobe.com/coldfusion/8/htmldocs/help.html?content=appFramework_04.html) for `/resources` so that Taffy can find your resources to initialize the routes.
```js
```cfscript
this.mappings["/resources"] = expandPath("./resources");
```
You'll also need to add a mapping for `/taffy` so that the resources can extend `taffy.core.resource` (since the taffy folder isn't a child of the resources folder):
```js
```cfscript
this.mappings["/taffy"] = expandPath("./taffy");
```
@@ -132,7 +132,7 @@ In the xml above you can see that I only have 1 wildcard, but to compensate I've
Taffy allows for setting the HTTP status message using [.withStatus()](#withstatus), such as:
```js
```cfscript
return representationOf({...}).withStatus(403, "Not Authorized");
```
@@ -221,7 +221,7 @@ If you want a resource not to be included in the dashboard, set `taffy:dashboard
or
```js
```cfscript
component taffy_uri="/secret-squirrel" taffy_dashboard_hide {
}
```
@@ -247,7 +247,7 @@ The **taffy:uri** property applies to the `<cfcomponent>` tag or the `component{
or
```js
```cfscript
component taffy_uri="/artist/{artistId}" {
}
```
@@ -278,7 +278,7 @@ By convention, resources will automatically map the 4 primary HTTP REST verbs --
or
```js
```cfscript
function getUser( numeric userId ) taffy_verb="get" {
}
```
@@ -294,7 +294,7 @@ You can prevent a resource from showing in the documentation by adding the `taff
or
```js
```cfscript
component extends="taffy.core.resource" taffy_uri="/artist/{artistId}" taffy_docs_hide {
}
```
@@ -310,7 +310,7 @@ Similarly, you can hide methods and parameters by adding the attribute to the `<
or
```js
```cfscript
public function getUser(
required numeric userId,
string _hidden = "" taffy_docs_hide
@@ -335,7 +335,7 @@ By convention, the mime-types supported by your API are determined by the method
or
```js
```cfscript
function getAsJson() taffy_mime="application/json" {
}
```
@@ -354,7 +354,7 @@ When your API supports more than one data format (i.e. json and xml), you must s
or
```js
```cfscript
function getAsJson() taffy_mime="application/json" taffy_default="true" {}
function getAsXml() taffy_mime="application/xml" {}
@@ -364,7 +364,7 @@ function getAsXml() taffy_mime="application/xml" {}
Default values:
```js
```cfscript
variables.framework = {
reloadKey = "reload",
reloadPassword = "true",
@@ -570,7 +570,7 @@ The allowed verbs, of course, are the ones allowed by the requested resource, as
In addition, as of Taffy 3.1.0, you can set this setting to a string of allowable hosts, as a (comma, semicolon, or space) delimited list:
```js
```cfscript
variables.framework.allowCrossDomain =
"http://example.com; http://foo.bar, http://google.com";
```
@@ -637,7 +637,7 @@ Global headers are static. You set them on application initialization and they d
**NOTE FOR EXTERNAL BEAN FACTORY USERS (e.g. Coldspring, DI/1 etc)** If your external bean factory is initialized in onApplicationStart then you need to set the variables.framework.beanFactory after your bean factory has initialized, for example:
```js
```cfscript
component extends="taffy.core.api"
{
this.name = 'myapi';
@@ -710,7 +710,7 @@ Taffy calls this method during initialization to determine in which configured e
The returned value will be used to load environment-specific configuration. For example, if you have the following code in your Application.cfc, then the dashboard will be disabled in production:
```js
```cfscript
variables.framework = {
disableDashboard = false
@@ -769,13 +769,13 @@ This method is optional, and allows you to inspect and potentially abort an API
If you do not return TRUE, allowing the request to continue as normal, then Taffy expects you to call and return the result of either **[noData()](#nodata)** or **[representationOf()](#representationof)**. If you simply want to return with a status code of 403 (which indicates "Not Allowed") and no response body, you could do this:
```js
```cfscript
return noData().withStatus(403);
```
Alternately, you could return some data to indicate that they owe you money or something:
```js
```cfscript
return representationOf({
error="Your account is past due. Please email accounts payable."
})
@@ -786,7 +786,7 @@ The options here are limited only by your imagination.
You can add data to the **requestArguments** structure and this will be passed on to any resource that handles the request. Simply add a key to the structure:
```js
```cfscript
function onTaffyRequest(
verb,
cfc,
@@ -803,7 +803,7 @@ function onTaffyRequest(
In your resource:
```js
```cfscript
function get(myData) {
//arguments.myData => "myValue"
}
@@ -811,13 +811,13 @@ function get(myData) {
You can use the method metadata for anything you see fit; but the original use case was for role-based security. Consider the following resource method:
```js
```cfscript
public function getData( id ) taffy_method="get" role="datareader" { ... }
```
Taffy doesn't do anything with the **role** metadata on this method other than expose it to you in onTaffyRequest. So let's use the user's API key to find out what roles they have, and verify that the method's required role is among them. This is a snippet from your Application.cfc:
```js
```cfscript
function onTaffyRequest(verb, cfc, requestArgs, mime, head, methodMetadata){
local.user = (...); //get user from api key...
@@ -914,7 +914,7 @@ This method saves data in a way that makes it available to [exception log adapte
This is a convenience method used to make sure that certain all-numeric inputs get serialized as a string in the output not converted to numeric output. Examples are postal codes or phone numbers.
```js
```cfscript
return rep(
queryToArray(myQuery, function (row) {
row.phone = encode.string(row.phone);
@@ -938,7 +938,7 @@ Behaves like `noData()` except that it sets the status code to 204 and the Conte
This method allows you to specify that there is no data to be returned for the current request. Generally, you would use it in conjunction with the **withStatus** method to set a specific return status for the request. For example, if the requested resource doesn't exist, you could return a 404 error like so:
```js
```cfscript
return noData().withStatus(404);
```
@@ -956,7 +956,7 @@ This method transforms a ColdFusion query object into an array of structures. It
The callback function can be used to efficiently transform keys in the structure before it is added to the array without additional looping. For example, you can use it to format dates in a particular style. **Your callback must return a value.**
```js
```cfscript
queryToArray(someQ, function (row) {
row.startDate = dateFormat(row.startDate, "yyyy-mm-dd");
return row;
@@ -965,7 +965,7 @@ queryToArray(someQ, function (row) {
When you want to return the resulting array as your API response, you must still wrap it in a [representationOf](#rep-1) call:
```js
```cfscript
return rep(queryToArray(someQ));
```
@@ -983,7 +983,7 @@ This method transforms a ColdFusion query object into a structure. If there is m
The callback function is passed the column name and the value, and can be used to efficiently transform keys in the structure as they are read from the query without need for additional looping. For example, you can use it to format dates in a particular style. **Your callback must return a value.**
```js
```cfscript
return queryToStruct(someQ, function (colName, val) {
if (colName == "startDate") {
return dateFormat(val, "yyyy-mm-dd");
@@ -1031,7 +1031,7 @@ If you don't configure a logging adapter, the default is LogToEmail, but the def
Use this method in place of `representationOf()` to return a stream of binary data. Useful for streaming things like dynamically generated PDFs. **Note: ** When streaming binary data as the response, you must set the mime type manually using [withMime()](#withmime). For example:
```js
```cfscript
return streamBinary(local.pdf).withStatus(200).withMime("application/pdf");
```
@@ -1044,7 +1044,7 @@ return streamBinary(local.pdf).withStatus(200).withMime("application/pdf");
Use this method in place of `representationOf()` to stream a file from disk (or VFS). Optionally append `.andDelete( true )` to delete the file once streaming is complete. **Note: ** When streaming binary data as the response, you must set the mime type manually using [withMime()](#withmime). For example:
```js
```cfscript
return streamFile("/foo.txt")
.andDelete(true)
.withStatus(200)
@@ -1069,7 +1069,7 @@ Use this method in place of `representationOf()` to stream an image from disk (o
This special method _**requires**_ the use of either **noData** or **representationOf**. It adds custom headers to the return. Additional use of **withStatus** optional.
```js
```cfscript
return representationOf(myData).withHeaders({"X-POWERED-BY"="Taffy 2.0!"});
```
@@ -1082,7 +1082,7 @@ return representationOf(myData).withHeaders({"X-POWERED-BY"="Taffy 2.0!"});
This special method _**requires**_ the use of either **streamFile** or **streamBinary**. It overrides the default mime type header for the return.
```js
```cfscript
return streamFile("kittens/cuteness.pdf").withMime("application/pdf");
```
@@ -1098,7 +1098,7 @@ This special method _**requires**_ the use of either **noData** or **representat
_If you do not specify a return status code, Taffy will always return status code 200 (OK) by default._
```js
```cfscript
return noData().withStatus(404, "Not Found");
```
+32 -32
View File
@@ -12,7 +12,7 @@ The REST Web Service framework for ColdFusion and Lucee
**Application.cfc:**
```js
```cfscript
component extends="taffy.core.api" {}
```
@@ -24,7 +24,7 @@ component extends="taffy.core.api" {}
**/resources/hello.cfc:**
```js
```cfscript
component extends="taffy.core.resource" taffy_uri="/hello" {
function get(){
@@ -77,13 +77,13 @@ You can see that the taffy folder is a sibling to Application.cfc. This allows A
Next, if your Application.cfc and `/resources/` folder aren't in the web-root (e.g. they're inside something like `/api/`) then you'll need to add an [application-specific mapping](http://livedocs.adobe.com/coldfusion/8/htmldocs/help.html?content=appFramework_04.html) for `/resources` so that Taffy can find your resources to initialize the routes.
```js
```cfscript
this.mappings["/resources"] = expandPath("./resources");
```
You'll also need to add a mapping for `/taffy` so that the resources can extend `taffy.core.resource` (since the taffy folder isn't a child of the resources folder):
```js
```cfscript
this.mappings["/taffy"] = expandPath("./taffy");
```
@@ -132,7 +132,7 @@ In the xml above you can see that I only have 1 wildcard, but to compensate I've
Taffy allows for setting the HTTP status message using [.withStatus()](#withstatus), such as:
```js
```cfscript
return representationOf({...}).withStatus(403, "Not Authorized");
```
@@ -221,7 +221,7 @@ If you want a resource not to be included in the dashboard, set `taffy:dashboard
or
```js
```cfscript
component taffy_uri="/secret-squirrel" taffy_dashboard_hide {
}
```
@@ -247,7 +247,7 @@ The **taffy:uri** property applies to the `<cfcomponent>` tag or the `component{
or
```js
```cfscript
component taffy_uri="/artist/{artistId}" {
}
```
@@ -278,7 +278,7 @@ By convention, resources will automatically map the 4 primary HTTP REST verbs --
or
```js
```cfscript
function getUser( numeric userId ) taffy_verb="get" {
}
```
@@ -294,7 +294,7 @@ You can prevent a resource from showing in the documentation by adding the `taff
or
```js
```cfscript
component extends="taffy.core.resource" taffy_uri="/artist/{artistId}" taffy_docs_hide {
}
```
@@ -310,7 +310,7 @@ Similarly, you can hide methods and parameters by adding the attribute to the `<
or
```js
```cfscript
public function getUser(
required numeric userId,
string _hidden = "" taffy_docs_hide
@@ -335,7 +335,7 @@ By convention, the mime-types supported by your API are determined by the method
or
```js
```cfscript
function getAsJson() taffy_mime="application/json" {
}
```
@@ -354,7 +354,7 @@ When your API supports more than one data format (i.e. json and xml), you must s
or
```js
```cfscript
function getAsJson() taffy_mime="application/json" taffy_default="true" {}
function getAsXml() taffy_mime="application/xml" {}
@@ -364,7 +364,7 @@ function getAsXml() taffy_mime="application/xml" {}
Default values:
```js
```cfscript
variables.framework = {
reloadKey = "reload",
reloadPassword = "true",
@@ -570,7 +570,7 @@ The allowed verbs, of course, are the ones allowed by the requested resource, as
In addition, as of Taffy 3.1.0, you can set this setting to a string of allowable hosts, as a (comma, semicolon, or space) delimited list:
```js
```cfscript
variables.framework.allowCrossDomain =
"http://example.com; http://foo.bar, http://google.com";
```
@@ -637,7 +637,7 @@ Global headers are static. You set them on application initialization and they d
**NOTE FOR EXTERNAL BEAN FACTORY USERS (e.g. Coldspring, DI/1 etc)** If your external bean factory is initialized in onApplicationStart then you need to set the variables.framework.beanFactory after your bean factory has initialized, for example:
```js
```cfscript
component extends="taffy.core.api"
{
this.name = 'myapi';
@@ -710,7 +710,7 @@ Taffy calls this method during initialization to determine in which configured e
The returned value will be used to load environment-specific configuration. For example, if you have the following code in your Application.cfc, then the dashboard will be disabled in production:
```js
```cfscript
variables.framework = {
disableDashboard = false
@@ -769,13 +769,13 @@ This method is optional, and allows you to inspect and potentially abort an API
If you do not return TRUE, allowing the request to continue as normal, then Taffy expects you to call and return the result of either **[noData()](#nodata)** or **[representationOf()](#representationof)**. If you simply want to return with a status code of 403 (which indicates "Not Allowed") and no response body, you could do this:
```js
```cfscript
return noData().withStatus(403);
```
Alternately, you could return some data to indicate that they owe you money or something:
```js
```cfscript
return representationOf({
error="Your account is past due. Please email accounts payable."
})
@@ -786,7 +786,7 @@ The options here are limited only by your imagination.
You can add data to the **requestArguments** structure and this will be passed on to any resource that handles the request. Simply add a key to the structure:
```js
```cfscript
function onTaffyRequest(
verb,
cfc,
@@ -803,7 +803,7 @@ function onTaffyRequest(
In your resource:
```js
```cfscript
function get(myData) {
//arguments.myData => "myValue"
}
@@ -811,13 +811,13 @@ function get(myData) {
You can use the method metadata for anything you see fit; but the original use case was for role-based security. Consider the following resource method:
```js
```cfscript
public function getData( id ) taffy_method="get" role="datareader" { ... }
```
Taffy doesn't do anything with the **role** metadata on this method other than expose it to you in onTaffyRequest. So let's use the user's API key to find out what roles they have, and verify that the method's required role is among them. This is a snippet from your Application.cfc:
```js
```cfscript
function onTaffyRequest(verb, cfc, requestArgs, mime, head, methodMetadata){
local.user = (...); //get user from api key...
@@ -914,7 +914,7 @@ This method saves data in a way that makes it available to [exception log adapte
This is a convenience method used to make sure that certain all-numeric inputs get serialized as a string in the output not converted to numeric output. Examples are postal codes or phone numbers.
```js
```cfscript
return rep(
queryToArray(myQuery, function (row) {
row.phone = encode.string(row.phone);
@@ -938,7 +938,7 @@ Behaves like `noData()` except that it sets the status code to 204 and the Conte
This method allows you to specify that there is no data to be returned for the current request. Generally, you would use it in conjunction with the **withStatus** method to set a specific return status for the request. For example, if the requested resource doesn't exist, you could return a 404 error like so:
```js
```cfscript
return noData().withStatus(404);
```
@@ -956,7 +956,7 @@ This method transforms a ColdFusion query object into an array of structures. It
The callback function can be used to efficiently transform keys in the structure before it is added to the array without additional looping. For example, you can use it to format dates in a particular style. **Your callback must return a value.**
```js
```cfscript
queryToArray(someQ, function (row) {
row.startDate = dateFormat(row.startDate, "yyyy-mm-dd");
return row;
@@ -965,7 +965,7 @@ queryToArray(someQ, function (row) {
When you want to return the resulting array as your API response, you must still wrap it in a [representationOf](#rep-1) call:
```js
```cfscript
return rep(queryToArray(someQ));
```
@@ -983,7 +983,7 @@ This method transforms a ColdFusion query object into a structure. If there is m
The callback function is passed the column name and the value, and can be used to efficiently transform keys in the structure as they are read from the query without need for additional looping. For example, you can use it to format dates in a particular style. **Your callback must return a value.**
```js
```cfscript
return queryToStruct(someQ, function (colName, val) {
if (colName == "startDate") {
return dateFormat(val, "yyyy-mm-dd");
@@ -1031,7 +1031,7 @@ If you don't configure a logging adapter, the default is LogToEmail, but the def
Use this method in place of `representationOf()` to return a stream of binary data. Useful for streaming things like dynamically generated PDFs. **Note: ** When streaming binary data as the response, you must set the mime type manually using [withMime()](#withmime). For example:
```js
```cfscript
return streamBinary(local.pdf).withStatus(200).withMime("application/pdf");
```
@@ -1044,7 +1044,7 @@ return streamBinary(local.pdf).withStatus(200).withMime("application/pdf");
Use this method in place of `representationOf()` to stream a file from disk (or VFS). Optionally append `.andDelete( true )` to delete the file once streaming is complete. **Note: ** When streaming binary data as the response, you must set the mime type manually using [withMime()](#withmime). For example:
```js
```cfscript
return streamFile("/foo.txt")
.andDelete(true)
.withStatus(200)
@@ -1069,7 +1069,7 @@ Use this method in place of `representationOf()` to stream an image from disk (o
This special method _**requires**_ the use of either **noData** or **representationOf**. It adds custom headers to the return. Additional use of **withStatus** optional.
```js
```cfscript
return representationOf(myData).withHeaders({"X-POWERED-BY"="Taffy 2.0!"});
```
@@ -1082,7 +1082,7 @@ return representationOf(myData).withHeaders({"X-POWERED-BY"="Taffy 2.0!"});
This special method _**requires**_ the use of either **streamFile** or **streamBinary**. It overrides the default mime type header for the return.
```js
```cfscript
return streamFile("kittens/cuteness.pdf").withMime("application/pdf");
```
@@ -1098,7 +1098,7 @@ This special method _**requires**_ the use of either **noData** or **representat
_If you do not specify a return status code, Taffy will always return status code 200 (OK) by default._
```js
```cfscript
return noData().withStatus(404, "Not Found");
```
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
+178 -41
View File
@@ -12,7 +12,7 @@ The REST Web Service framework for ColdFusion and Lucee
**Application.cfc:**
```js
```cfscript
component extends="taffy.core.api" {}
```
@@ -24,7 +24,7 @@ component extends="taffy.core.api" {}
**/resources/hello.cfc:**
```js
```cfscript
component extends="taffy.core.resource" taffy_uri="/hello" {
function get(){
@@ -75,15 +75,19 @@ Using sub-folders requires the use of Application-Specific Mappings (introduced
You can see that the taffy folder is a sibling to Application.cfc. This allows Application.cfc to use relative paths to extend `taffy.core.api`.
Next, if your Application.cfc and `/resources/` folder aren't in the web-root (e.g. they're inside something like `/api/`) then you'll need to add an [application-specific mapping](http://livedocs.adobe.com/coldfusion/8/htmldocs/help.html?content=appFramework_04.html) for `/resources` so that Taffy can find your resources to initialize the routes.
Next, if your Application.cfc and `/resources/` folder aren't in the web-root (e.g. they're inside something like `/api/`) then you'll need to do one of the following:
```js
this.mappings["/resources"] = expandPath("./resources");
```
- Add an [application-specific mapping](http://livedocs.adobe.com/coldfusion/8/htmldocs/help.html?content=appFramework_04.html) for `/resources` so that Taffy can find your resources to initialize the routes.
```cfscript
this.mappings["/resources"] = expandPath("../api/resources");
```
- You can specify the path to your resource components using the [`resourcesCFCPath`](#resourcescfcpath) setting to specify the dotted path to your resource folder. This will allow you to store your resource components in the directory of your choosing.
You'll also need to add a mapping for `/taffy` so that the resources can extend `taffy.core.resource` (since the taffy folder isn't a child of the resources folder):
```js
```cfscript
this.mappings["/taffy"] = expandPath("./taffy");
```
@@ -132,7 +136,7 @@ In the xml above you can see that I only have 1 wildcard, but to compensate I've
Taffy allows for setting the HTTP status message using [.withStatus()](#withstatus), such as:
```js
```cfscript
return representationOf({...}).withStatus(403, "Not Authorized");
```
@@ -173,6 +177,54 @@ _Thanks to Brook Davies for providing the solution!_
Not all HTTP clients will allow you to easily send PUT or DELETE requests (sometimes not at all). The standard method for circumventing this restriction, which Taffy supports, is by sending your request as a POST with the header `X-HTTP-METHOD-OVERRIDE` and setting its value to PUT/DELETE as needed. Taffy will detect this header and treat the request as if it were a PUT/DELETE request.
## OpenAPI / Swagger
**Available in:** Taffy 4.0+
Taffy generates an [OpenAPI 3.1](https://spec.openapis.org/oas/v3.1.0) document describing your API automatically, using the same resource/argument metadata that powers the dashboard and docs. No extra annotations required — just hit:
- `index.cfm?openapi` — returns the spec as `application/json`
- `index.cfm?swagger` — alias for the same endpoint
The generated document can be dropped into [Swagger UI](https://swagger.io/tools/swagger-ui/), [Stoplight Studio](https://stoplight.io/studio), Postman, [swagger.io Editor](https://editor.swagger.io/), or any other OpenAPI-aware tool.
### What gets generated
| OpenAPI field | Source |
| ------------------------------------------ | ---------------------------------------------------------------------------------------------------------------------- |
| `info.title` | `variables.framework.docs.APIName` |
| `info.version` | `variables.framework.docs.APIVersion` |
| `info.description` / `contact` / `license` | `variables.framework.openapi` (optional) |
| `servers[0].url` | Auto-derived from `cgi.http_host` + script path (override via `openapi.servers`) |
| `paths` | Each resource's `taffy:uri` |
| `operationId` | `{beanName}_{verb}` |
| `summary` / `tags` | `taffy:docs:name` if set, else the bean name |
| `description` | Function `hint` |
| `parameters` (path) | URI tokens — always emitted, even if the resource doesn't declare them as args |
| `parameters` (query) | Non-token args on `GET`/`DELETE`/`HEAD`/`OPTIONS` methods |
| `requestBody` | Non-token args on `POST`/`PUT`/`PATCH` — emitted under both `application/json` and `application/x-www-form-urlencoded` |
| Parameter `schema.type` / `format` | Mapped from the CFML arg `type` (`numeric`, `boolean`, `date`, `uuid`, etc.) |
| Parameter `description` | Argument `hint` |
| Parameter `default` | Argument `default` (when non-empty) |
| Response content types | MIME types from all registered serializers (via `taffy:mime`) |
### Hiding things from the spec
The same metadata flags that hide resources from the dashboard also hide them from the OpenAPI spec:
- `taffy:docs:hide` / `taffy_docs_hide` — component, function, or argument
- `taffy:dashboard:hide` / `taffy_dashboard_hide` — component, function, or argument
Custom `taffy:verb` values (anything outside the standard `get`/`put`/`post`/`delete`/`options`/`head`/`patch`/`trace`) are silently dropped — OpenAPI doesn't accept arbitrary verb names.
### Performance
The spec is generated once on the first request to `?openapi` (or `?swagger`) and cached in `application._taffy`. Subsequent requests serve the cached JSON string directly — no CFC instantiation, no metadata walking, no serialization. The cache is invalidated automatically on framework reload.
### Configuration
See [openapi](#openapi) below for the full config reference.
## More Guides
Some guides are too broad for this document. For your benefit, they are linked here:
@@ -221,7 +273,7 @@ If you want a resource not to be included in the dashboard, set `taffy:dashboard
or
```js
```cfscript
component taffy_uri="/secret-squirrel" taffy_dashboard_hide {
}
```
@@ -247,7 +299,7 @@ The **taffy:uri** property applies to the `<cfcomponent>` tag or the `component{
or
```js
```cfscript
component taffy_uri="/artist/{artistId}" {
}
```
@@ -278,7 +330,7 @@ By convention, resources will automatically map the 4 primary HTTP REST verbs --
or
```js
```cfscript
function getUser( numeric userId ) taffy_verb="get" {
}
```
@@ -294,7 +346,7 @@ You can prevent a resource from showing in the documentation by adding the `taff
or
```js
```cfscript
component extends="taffy.core.resource" taffy_uri="/artist/{artistId}" taffy_docs_hide {
}
```
@@ -310,7 +362,7 @@ Similarly, you can hide methods and parameters by adding the attribute to the `<
or
```js
```cfscript
public function getUser(
required numeric userId,
string _hidden = "" taffy_docs_hide
@@ -320,6 +372,27 @@ taffy_docs_hide
}
```
##### Argument Constraint Metadata
You can document value constraints on function arguments using the following metadata attributes. These are **documentation-only** — Taffy does not enforce them at runtime, but they are displayed on the dashboard and generated docs as inline badges next to the argument name.
| Attribute | Applies to | Description |
| ----------------- | ---------- | ---------------------------------- |
| `taffy_minlength` | string | Minimum string length |
| `taffy_maxlength` | string | Maximum string length |
| `taffy_min` | numeric | Minimum numeric value |
| `taffy_max` | numeric | Maximum numeric value |
| `taffy_pattern` | string | Regex pattern the value must match |
```cfscript
function get(
required string changedOnDate = "" taffy_minlength="10" taffy_maxlength="10" taffy_pattern="^\d{4}-\d{2}-\d{2}$",
numeric page = 1 taffy_min="1",
numeric pageSize = 25 taffy_min="1" taffy_max="100"
) {
}
```
#### In Serializers
Serializers are used to take the data provided by a resource and serialize it into a format usable by the web service consumer. A single Serializer is capable of serializing native data objects (strings, numbers, queries, structures, arrays, etc) into 1 or more formats. Typical formats include JSON, XML, or YAML, but are not limited.
@@ -335,7 +408,7 @@ By convention, the mime-types supported by your API are determined by the method
or
```js
```cfscript
function getAsJson() taffy_mime="application/json" {
}
```
@@ -354,7 +427,7 @@ When your API supports more than one data format (i.e. json and xml), you must s
or
```js
```cfscript
function getAsJson() taffy_mime="application/json" taffy_default="true" {}
function getAsXml() taffy_mime="application/xml" {}
@@ -364,8 +437,9 @@ function getAsXml() taffy_mime="application/xml" {}
Default values:
```js
```cfscript
variables.framework = {
resourcesCFCPath = "",
reloadKey = "reload",
reloadPassword = "true",
reloadOnEveryRequest = false,
@@ -382,6 +456,7 @@ variables.framework = {
disabledDashboardRedirect = "",
dashboardHeaders = {},
showDocsWhenDashboardDisabled = false,
allowGoogleFonts = true,
docs = {
APIName = "",
APIVersion = ""
@@ -396,21 +471,33 @@ variables.framework = {
unhandledPaths = "/flex2gateway",
allowCrossDomain = false,
exposeTaffyHeaders = true,
globalHeaders = structNew(),
debugKey = "debug",
useEtags = false,
returnExceptionsAsJson = true,
returnExceptionsAsJson = false,
exceptionLogAdapter = "taffy.bonus.LogToScreen",
exceptionLogAdapterConfig = {},
beanFactory = "",
openapi = {
enabled = true
},
environments = {}
};
```
#### resourcesCFCPath
**Available in:** Taffy 3.8+<br/>
**Type:** String<br/>
**Default:** ""<br/>
**Description:** By default, Taffy will attempt to load your resource components from either a child folder named `resources` or from a CF mapping named `resources`. You can use this setting to define an explicit path to your resource components using the "dotted" path of your resource folder (e.g. `myapp.api.rest-cfcs`).
#### reloadKey
**Available in:** Taffy 1.2+<br/>
@@ -497,6 +584,13 @@ To provide a simulated response, add an additional method to your Resource CFCs
**Default:** False<br/>
**Description:** Whether or not Taffy will display user friendly documentation when the dashboard is disabled.
#### allowGoogleFonts
**Available in:** Taffy 4.0+<br/>
**Type:** Boolean<br/>
**Default:** True<br/>
**Description:** When true, the dashboard and documentation pages load the [Atkinson Hyperlegible](https://fonts.google.com/specimen/Atkinson+Hyperlegible) and [Atkinson Hyperlegible Mono](https://fonts.google.com/specimen/Atkinson+Hyperlegible+Mono) fonts from Google Fonts. Set to false to prevent any external requests to Google, in which case the dashboard falls back to system fonts.
#### docs.APIName
**Available in:** Taffy 3.0+<br/>
@@ -570,11 +664,32 @@ The allowed verbs, of course, are the ones allowed by the requested resource, as
In addition, as of Taffy 3.1.0, you can set this setting to a string of allowable hosts, as a (comma, semicolon, or space) delimited list:
```js
```cfscript
variables.framework.allowCrossDomain =
"http://example.com; http://foo.bar, http://google.com";
```
#### exposeTaffyHeaders
**Available in:** Taffy 3.8+<br/>
**Type:** Boolean<br/>
**Default:** true<br/>
**Description:** Determines if the standard Taffy debug HTTP response headers should be included with each request. The Taffy debug headers are:
- `X-TAFFY-RELOADED` — Determines if the Taffy configuration was reloaded on the request.
- `X-TIME-TO-RELOAD` — The time it took for Taffy to initialize.
- `X-TIME-IN-PARSE` — The time it took to parse the request.
- `X-TIME-IN-ONTAFFYREQUEST` — The time spent in the `onTaffyRequest` method.
- `X-TIME-IN-RESOURCE` — The time spent executing the requested resource.
- `X-TIME-IN-CACHE-CHECK` — The time spent checking for a cached response.
- `X-TIME-IN-CACHE-GET` — The time spent to retrieve a cached response.
- `X-TIME-IN-CACHE-SAVE` — The time spent to save a cached response.
- `X-TIME-IN-SERIALIZE` — The time spent serializing the response.
- `X-TIME-IN-TAFFY` — The time spent executing the Taffy internals.
- `X-TIME-IN-ONTAFFYREQUESTEND` — The time spent in the `onTaffyRequestEnd` method.
Setting this to `false` will prevent these response headers from being written to the HTTP stream.
#### globalHeaders
**Available in:** Taffy 1.2+<br/>
@@ -611,15 +726,15 @@ Global headers are static. You set them on application initialization and they d
**Available in:** Taffy 1.2+<br/>
**Type:** Boolean<br/>
**Default:** true<br/>
**Description:** When an error occurs that is not otherwise handled, this option tells Taffy to attempt to format the error information as JSON and return that (regardless of the requested return format). As of Taffy 2.1 this also includes a structured stack trace with file names and line numbers.
**Default:** false<br/>
**Description:** When an error occurs that is not otherwise handled, this option tells Taffy to attempt to format the error information as JSON and return that (regardless of the requested return format). As of Taffy 2.1 this also includes a structured stack trace with file names and line numbers. Default changed to `false` in Taffy 4.x to prevent accidental stack trace disclosure in production.
#### exceptionLogAdapter
**Available in:** Taffy 1.2+<br/>
**Type:** String<br/>
**Default:** "taffy.bonus.LogToEmail"<br/>
**Description:** CFC dot-notation path to the exception logging adapter you want to use. Default adapter simply emails all exceptions. See [Exception Logging Adapters](https://github.com/atuttle/Taffy/wiki/Exception-Logging-Adapters) for more details.
**Default:** "taffy.bonus.LogToDevNull"<br/>
**Description:** CFC dot-notation path to the exception logging adapter you want to use. Default adapter discards all exceptions silently. See [Exception Logging Adapters](https://github.com/atuttle/Taffy/wiki/Exception-Logging-Adapters) for more details.
#### exceptionLogAdapterConfig
@@ -637,7 +752,7 @@ Global headers are static. You set them on application initialization and they d
**NOTE FOR EXTERNAL BEAN FACTORY USERS (e.g. Coldspring, DI/1 etc)** If your external bean factory is initialized in onApplicationStart then you need to set the variables.framework.beanFactory after your bean factory has initialized, for example:
```js
```cfscript
component extends="taffy.core.api"
{
this.name = 'myapi';
@@ -652,6 +767,28 @@ component extends="taffy.core.api"
}
```
#### openapi
**Available in:** Taffy 4.0+<br/>
**Type:** Structure<br/>
**Default:** `{ enabled: true }`<br/>
**Description:** Controls the OpenAPI 3.1 spec endpoint served at `?openapi` / `?swagger`. Set `enabled` to `false` to disable the endpoint entirely (returns 403). Optional keys `description`, `contact`, and `license` are passed through to the spec's `info` block. `servers` (array of `{ url, description }` objects) overrides the auto-derived server URL.
```cfscript
variables.framework.openapi = {
enabled = true,
description = "Public API for the Foo platform.",
contact = { name = "API Team", email = "api@example.com" },
license = { name = "MIT", url = "https://opensource.org/licenses/MIT" },
servers = [
{ url = "https://api.example.com", description = "Production" },
{ url = "https://staging.api.example.com", description = "Staging" }
]
};
```
See the [OpenAPI / Swagger](#openapi--swagger) section for details on what gets generated.
#### environments
**Available in:** Taffy 1.3+<br/>
@@ -710,7 +847,7 @@ Taffy calls this method during initialization to determine in which configured e
The returned value will be used to load environment-specific configuration. For example, if you have the following code in your Application.cfc, then the dashboard will be disabled in production:
```js
```cfscript
variables.framework = {
disableDashboard = false
@@ -769,13 +906,13 @@ This method is optional, and allows you to inspect and potentially abort an API
If you do not return TRUE, allowing the request to continue as normal, then Taffy expects you to call and return the result of either **[noData()](#nodata)** or **[representationOf()](#representationof)**. If you simply want to return with a status code of 403 (which indicates "Not Allowed") and no response body, you could do this:
```js
```cfscript
return noData().withStatus(403);
```
Alternately, you could return some data to indicate that they owe you money or something:
```js
```cfscript
return representationOf({
error="Your account is past due. Please email accounts payable."
})
@@ -786,7 +923,7 @@ The options here are limited only by your imagination.
You can add data to the **requestArguments** structure and this will be passed on to any resource that handles the request. Simply add a key to the structure:
```js
```cfscript
function onTaffyRequest(
verb,
cfc,
@@ -803,7 +940,7 @@ function onTaffyRequest(
In your resource:
```js
```cfscript
function get(myData) {
//arguments.myData => "myValue"
}
@@ -811,13 +948,13 @@ function get(myData) {
You can use the method metadata for anything you see fit; but the original use case was for role-based security. Consider the following resource method:
```js
```cfscript
public function getData( id ) taffy_method="get" role="datareader" { ... }
```
Taffy doesn't do anything with the **role** metadata on this method other than expose it to you in onTaffyRequest. So let's use the user's API key to find out what roles they have, and verify that the method's required role is among them. This is a snippet from your Application.cfc:
```js
```cfscript
function onTaffyRequest(verb, cfc, requestArgs, mime, head, methodMetadata){
local.user = (...); //get user from api key...
@@ -914,7 +1051,7 @@ This method saves data in a way that makes it available to [exception log adapte
This is a convenience method used to make sure that certain all-numeric inputs get serialized as a string in the output not converted to numeric output. Examples are postal codes or phone numbers.
```js
```cfscript
return rep(
queryToArray(myQuery, function (row) {
row.phone = encode.string(row.phone);
@@ -938,7 +1075,7 @@ Behaves like `noData()` except that it sets the status code to 204 and the Conte
This method allows you to specify that there is no data to be returned for the current request. Generally, you would use it in conjunction with the **withStatus** method to set a specific return status for the request. For example, if the requested resource doesn't exist, you could return a 404 error like so:
```js
```cfscript
return noData().withStatus(404);
```
@@ -956,7 +1093,7 @@ This method transforms a ColdFusion query object into an array of structures. It
The callback function can be used to efficiently transform keys in the structure before it is added to the array without additional looping. For example, you can use it to format dates in a particular style. **Your callback must return a value.**
```js
```cfscript
queryToArray(someQ, function (row) {
row.startDate = dateFormat(row.startDate, "yyyy-mm-dd");
return row;
@@ -965,7 +1102,7 @@ queryToArray(someQ, function (row) {
When you want to return the resulting array as your API response, you must still wrap it in a [representationOf](#rep-1) call:
```js
```cfscript
return rep(queryToArray(someQ));
```
@@ -983,7 +1120,7 @@ This method transforms a ColdFusion query object into a structure. If there is m
The callback function is passed the column name and the value, and can be used to efficiently transform keys in the structure as they are read from the query without need for additional looping. For example, you can use it to format dates in a particular style. **Your callback must return a value.**
```js
```cfscript
return queryToStruct(someQ, function (colName, val) {
if (colName == "startDate") {
return dateFormat(val, "yyyy-mm-dd");
@@ -1020,7 +1157,7 @@ Data can be of any type, including complex data types like queries, structures,
What you pass to this method is simply handed off to the logging adapter. You may use one of the included adapters (LogToEmail, LogToBuglogHQ, LogToLog, or LogToHoth), or a custom logging adapter. If you write a custom logging adapter, it should implement the `taffy.bonus.ILogAdapter` interface.
If you don't configure a logging adapter, the default is LogToEmail, but the default `from` and `to` email addresses are not useful. See [Exception Log Adapters](https://github.com/atuttle/Taffy/wiki/Exception-Logging-Adapters) for more information on configuring logging adapters.
If you don't configure a logging adapter, the default is LogToDevNull, which silently discards exceptions. See [Exception Log Adapters](https://github.com/atuttle/Taffy/wiki/Exception-Logging-Adapters) for more information on configuring logging adapters.
#### streamBinary()
@@ -1031,7 +1168,7 @@ If you don't configure a logging adapter, the default is LogToEmail, but the def
Use this method in place of `representationOf()` to return a stream of binary data. Useful for streaming things like dynamically generated PDFs. **Note: ** When streaming binary data as the response, you must set the mime type manually using [withMime()](#withmime). For example:
```js
```cfscript
return streamBinary(local.pdf).withStatus(200).withMime("application/pdf");
```
@@ -1044,7 +1181,7 @@ return streamBinary(local.pdf).withStatus(200).withMime("application/pdf");
Use this method in place of `representationOf()` to stream a file from disk (or VFS). Optionally append `.andDelete( true )` to delete the file once streaming is complete. **Note: ** When streaming binary data as the response, you must set the mime type manually using [withMime()](#withmime). For example:
```js
```cfscript
return streamFile("/foo.txt")
.andDelete(true)
.withStatus(200)
@@ -1069,7 +1206,7 @@ Use this method in place of `representationOf()` to stream an image from disk (o
This special method _**requires**_ the use of either **noData** or **representationOf**. It adds custom headers to the return. Additional use of **withStatus** optional.
```js
```cfscript
return representationOf(myData).withHeaders({"X-POWERED-BY"="Taffy 2.0!"});
```
@@ -1082,7 +1219,7 @@ return representationOf(myData).withHeaders({"X-POWERED-BY"="Taffy 2.0!"});
This special method _**requires**_ the use of either **streamFile** or **streamBinary**. It overrides the default mime type header for the return.
```js
```cfscript
return streamFile("kittens/cuteness.pdf").withMime("application/pdf");
```
@@ -1098,7 +1235,7 @@ This special method _**requires**_ the use of either **noData** or **representat
_If you do not specify a return status code, Taffy will always return status code 200 (OK) by default._
```js
```cfscript
return noData().withStatus(404, "Not Found");
```
+12
View File
@@ -0,0 +1,12 @@
<!-- docs/_coverpage.md -->
# Taffy <small>v4</small>
> The REST web service framework for ColdFusion &amp; Lucee.
A full REST API in a single file. Opinionated defaults. Zero boilerplate.
Content negotiation, CORS, an interactive dashboard, and OpenAPI JSON — included.
[Get Started](#quickstart-your-first-api)
[Download v4.0.0](https://github.com/atuttle/Taffy/archive/v4.0.0.zip)
[GitHub &#8599;](https://github.com/atuttle/Taffy)
+924 -25
View File
@@ -2,46 +2,945 @@
<html lang="en">
<head>
<meta charset="UTF-8" />
<title>Document</title>
<title>Taffy — REST Framework for ColdFusion &amp; Lucee</title>
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1" />
<meta
name="description"
content="Documentation for Taffy — The REST Web Service Framework for ColdFusion and Lucee"
/>
<meta
name="viewport"
content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0"
content="Taffy is a REST web service framework for ColdFusion and Lucee. Write an API in a single file. Docs for every version."
/>
<meta name="viewport" content="width=device-width, initial-scale=1" />
<meta name="color-scheme" content="light dark" />
<link rel="icon" href="favicon.ico" />
<link rel="preconnect" href="https://fonts.googleapis.com" />
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
<link
href="https://fonts.googleapis.com/css2?family=Atkinson+Hyperlegible:ital,wght@0,400;0,700;1,400;1,700&family=JetBrains+Mono:wght@400;600&display=swap"
rel="stylesheet"
href="//cdn.jsdelivr.net/npm/docsify/lib/themes/buble.css"
/>
<style>
/* ---------- Theme tokens ---------- */
:root {
--font-sans: "Atkinson Hyperlegible", ui-sans-serif, system-ui,
-apple-system, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
--font-mono: "JetBrains Mono", ui-monospace, SFMono-Regular, Menlo,
Monaco, Consolas, "Liberation Mono", "Courier New", monospace;
/* Light palette (default) */
--bg: #fdf8f2;
--bg-elev: #ffffff;
--bg-sidebar: #f6ede0;
--bg-code: #f2e7d5;
--bg-inline-code: #f2e4cf;
--text: #1c1a18;
--text-muted: #6b5f52;
--text-subtle: #8b7f72;
--border: #e7d9c2;
--border-strong: #d9c6a8;
--accent: #c8396e; /* taffy pink */
--accent-hover: #a82557;
--accent-contrast: #ffffff;
--accent-soft: rgba(200, 57, 110, 0.12);
--warn: #b4651f;
--shadow: 0 1px 2px rgba(28, 26, 24, 0.04),
0 4px 12px rgba(28, 26, 24, 0.06);
--shadow-lg: 0 10px 30px rgba(28, 26, 24, 0.12);
--radius: 10px;
--radius-sm: 6px;
--radius-lg: 16px;
--content-width: 820px;
--sidebar-width: 280px;
}
@media (prefers-color-scheme: dark) {
:root {
--bg: #161310;
--bg-elev: #1f1a16;
--bg-sidebar: #1a1613;
--bg-code: #24201c;
--bg-inline-code: #2a241e;
--text: #f2e9dd;
--text-muted: #b3a796;
--text-subtle: #8a7e6f;
--border: #332c24;
--border-strong: #483d31;
--accent: #ff7aa8;
--accent-hover: #ff9ec0;
--accent-contrast: #1a0d14;
--accent-soft: rgba(255, 122, 168, 0.14);
--warn: #e3a36a;
--shadow: 0 1px 2px rgba(0, 0, 0, 0.4),
0 4px 12px rgba(0, 0, 0, 0.3);
--shadow-lg: 0 10px 30px rgba(0, 0, 0, 0.5);
}
img.logo-invert-on-dark {
filter: brightness(0.92);
}
}
/* ---------- Base ---------- */
* {
box-sizing: border-box;
}
html,
body {
margin: 0;
padding: 0;
background: var(--bg);
color: var(--text);
font-family: var(--font-sans);
font-size: 17px;
line-height: 1.65;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}
body {
min-height: 100dvh;
}
a {
color: var(--accent);
text-decoration: none;
border-bottom: 1px solid transparent;
transition: color 0.15s ease, border-color 0.15s ease;
}
a:hover {
color: var(--accent-hover);
border-bottom-color: var(--accent-hover);
}
::selection {
background: var(--accent-soft);
color: var(--text);
}
/* ---------- Sidebar ---------- */
.sidebar {
position: fixed;
top: 0;
left: 0;
bottom: 0;
width: var(--sidebar-width);
padding: 28px 22px 28px 28px;
background: var(--bg-sidebar);
border-right: 1px solid var(--border);
overflow-y: auto;
overflow-x: hidden;
z-index: 10;
transition: transform 0.25s ease;
}
.sidebar::-webkit-scrollbar {
width: 8px;
}
.sidebar::-webkit-scrollbar-thumb {
background: var(--border-strong);
border-radius: 8px;
}
.sidebar .app-name {
margin-bottom: 18px;
padding-bottom: 14px;
border-bottom: 1px solid var(--border);
}
.sidebar .app-name-link {
display: inline-flex;
align-items: center;
gap: 10px;
font-size: 20px;
font-weight: 700;
color: var(--text);
border-bottom: 0;
letter-spacing: -0.01em;
}
.sidebar .app-name-link::before {
content: "";
width: 26px;
height: 26px;
border-radius: 50%;
background: radial-gradient(
circle at 30% 30%,
#ff9abf,
var(--accent) 70%
);
box-shadow: inset 0 -3px 6px rgba(0, 0, 0, 0.15);
flex-shrink: 0;
}
.sidebar .search {
margin: 0 0 14px 0;
padding: 0 0 14px 0;
border-bottom: 1px solid var(--border);
}
.sidebar .search input {
width: 100%;
padding: 8px 10px;
font-family: var(--font-sans);
font-size: 14px;
color: var(--text);
background: var(--bg-elev);
border: 1px solid var(--border);
border-radius: var(--radius-sm);
outline: none;
}
.sidebar .search input:focus {
border-color: var(--accent);
box-shadow: 0 0 0 3px var(--accent-soft);
}
.sidebar-nav ul {
list-style: none;
margin: 0;
padding-left: 0;
}
.sidebar-nav > ul > li {
margin: 2px 0;
}
.sidebar-nav ul ul {
padding-left: 12px;
border-left: 1px solid var(--border);
margin-left: 4px;
}
.sidebar-nav a {
display: block;
padding: 5px 10px;
color: var(--text-muted);
font-size: 14.5px;
border-radius: var(--radius-sm);
border-bottom: 0;
line-height: 1.4;
}
.sidebar-nav a:hover {
color: var(--text);
background: var(--accent-soft);
}
.sidebar-nav a.active,
.sidebar-nav li.active > a {
color: var(--accent);
font-weight: 700;
background: var(--accent-soft);
}
.sidebar-nav p {
margin: 16px 0 4px;
padding: 0 10px;
font-size: 12px;
text-transform: uppercase;
letter-spacing: 0.08em;
color: var(--text-subtle);
font-weight: 700;
}
/* ---------- Sidebar toggle (mobile) ---------- */
.sidebar-toggle {
position: fixed;
top: 16px;
left: 16px;
z-index: 30;
width: 40px;
height: 40px;
display: none;
align-items: center;
justify-content: center;
background: var(--bg-elev);
border: 1px solid var(--border);
border-radius: var(--radius-sm);
cursor: pointer;
box-shadow: var(--shadow);
}
.sidebar-toggle .sidebar-toggle-button {
width: 18px;
height: 14px;
position: relative;
}
.sidebar-toggle span {
position: absolute;
left: 0;
right: 0;
height: 2px;
background: var(--text);
border-radius: 2px;
}
.sidebar-toggle span:nth-child(1) {
top: 0;
}
.sidebar-toggle span:nth-child(2) {
top: 6px;
}
.sidebar-toggle span:nth-child(3) {
top: 12px;
}
/* ---------- Content ---------- */
main {
display: block;
min-height: 100dvh;
}
.content {
margin-left: var(--sidebar-width);
padding: 0;
}
.markdown-section {
max-width: var(--content-width);
margin: 0 auto;
padding: 64px 40px 120px;
}
.markdown-section > :first-child {
margin-top: 0;
}
/* Typography */
.markdown-section h1,
.markdown-section h2,
.markdown-section h3,
.markdown-section h4,
.markdown-section h5,
.markdown-section h6 {
font-weight: 700;
letter-spacing: -0.015em;
line-height: 1.25;
color: var(--text);
}
.markdown-section h1 {
font-size: 2.4rem;
margin: 0 0 0.4em;
letter-spacing: -0.025em;
}
.markdown-section h2 {
font-size: 1.75rem;
margin: 2.2em 0 0.6em;
padding-bottom: 0.35em;
border-bottom: 1px solid var(--border);
}
.markdown-section h3 {
font-size: 1.3rem;
margin: 1.8em 0 0.5em;
}
.markdown-section h4 {
font-size: 1.1rem;
margin: 1.5em 0 0.4em;
}
.markdown-section p {
margin: 0.9em 0;
}
.markdown-section strong {
font-weight: 700;
}
.markdown-section blockquote {
margin: 1.2em 0;
padding: 0.1em 1.2em;
border-left: 4px solid var(--accent);
background: var(--accent-soft);
border-radius: 0 var(--radius-sm) var(--radius-sm) 0;
color: var(--text);
}
.markdown-section hr {
border: 0;
border-top: 1px solid var(--border);
margin: 2.5em 0;
}
/* Lists */
.markdown-section ul,
.markdown-section ol {
padding-left: 1.5em;
}
.markdown-section li {
margin: 0.25em 0;
}
.markdown-section li::marker {
color: var(--accent);
}
/* Code */
.markdown-section code {
font-family: var(--font-mono);
font-size: 0.88em;
padding: 2px 6px;
background: var(--bg-inline-code);
border-radius: 4px;
color: var(--text);
}
.markdown-section pre {
margin: 1.2em 0;
padding: 0;
background: var(--bg-code);
border: 1px solid var(--border);
border-radius: var(--radius);
overflow: hidden;
box-shadow: var(--shadow);
}
.markdown-section pre > code {
display: block;
padding: 16px 18px;
background: transparent;
font-family: var(--font-mono);
font-size: 14px;
line-height: 1.6;
overflow-x: auto;
border-radius: 0;
}
.markdown-section pre[data-lang]::before {
content: attr(data-lang);
display: block;
padding: 6px 14px;
font-size: 11px;
font-weight: 700;
letter-spacing: 0.08em;
text-transform: uppercase;
color: var(--text-subtle);
background: var(--bg-elev);
border-bottom: 1px solid var(--border);
}
/* ---------- Prism syntax highlighting (light mode) ---------- */
.token.comment,
.token.prolog,
.token.doctype,
.token.cdata {
color: #8a7f72;
font-style: italic;
}
.token.punctuation {
color: #5c524a;
}
.token.property,
.token.tag,
.token.boolean,
.token.number,
.token.constant,
.token.symbol,
.token.deleted {
color: #b4651f;
}
.token.selector,
.token.attr-name,
.token.string,
.token.char,
.token.builtin,
.token.inserted {
color: #6a8a3a;
}
.token.operator,
.token.entity,
.token.url,
.language-css .token.string,
.style .token.string {
color: #a82557;
}
.token.atrule,
.token.attr-value,
.token.keyword {
color: #c8396e;
font-weight: 600;
}
.token.function,
.token.class-name {
color: #2a6a9a;
}
.token.regex,
.token.important,
.token.variable {
color: #b4651f;
}
.token.important,
.token.bold {
font-weight: 700;
}
.token.italic {
font-style: italic;
}
/* ---------- Prism syntax highlighting (dark mode) ---------- */
@media (prefers-color-scheme: dark) {
.token.comment,
.token.prolog,
.token.doctype,
.token.cdata {
color: #8a7e6f;
}
.token.punctuation {
color: #b3a796;
}
.token.property,
.token.tag,
.token.boolean,
.token.number,
.token.constant,
.token.symbol,
.token.deleted {
color: #e3a36a;
}
.token.selector,
.token.attr-name,
.token.string,
.token.char,
.token.builtin,
.token.inserted {
color: #a8c97a;
}
.token.operator,
.token.entity,
.token.url,
.language-css .token.string,
.style .token.string {
color: #ff9ec0;
}
.token.atrule,
.token.attr-value,
.token.keyword {
color: #ff7aa8;
}
.token.function,
.token.class-name {
color: #7bb6e0;
}
.token.regex,
.token.important,
.token.variable {
color: #e3a36a;
}
}
/* Tables */
.markdown-section table {
width: 100%;
margin: 1.2em 0;
border-collapse: collapse;
display: block;
overflow-x: auto;
}
.markdown-section th,
.markdown-section td {
padding: 10px 14px;
text-align: left;
border-bottom: 1px solid var(--border);
}
.markdown-section th {
font-weight: 700;
background: var(--bg-sidebar);
}
.markdown-section tr:hover td {
background: var(--accent-soft);
}
/* Images */
.markdown-section img {
max-width: 100%;
border-radius: var(--radius);
}
/* ---------- Homepage extras ---------- */
.hero-badges {
display: flex;
flex-wrap: wrap;
gap: 8px;
margin: 0 0 1.5em;
}
.hero-badges .badge {
display: inline-flex;
align-items: center;
gap: 6px;
padding: 4px 10px;
font-size: 12.5px;
font-weight: 700;
background: var(--accent-soft);
color: var(--accent);
border-radius: 999px;
border-bottom: 0;
}
.hero-badges .badge:hover {
background: var(--accent);
color: var(--accent-contrast);
}
.feature-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(220px, 1fr));
gap: 14px;
margin: 1.6em 0 2em;
padding: 0;
list-style: none;
}
.feature-grid li {
margin: 0;
padding: 18px 20px;
background: var(--bg-elev);
border: 1px solid var(--border);
border-radius: var(--radius);
box-shadow: var(--shadow);
transition: transform 0.15s ease, box-shadow 0.15s ease,
border-color 0.15s ease;
}
.feature-grid li:hover {
transform: translateY(-2px);
box-shadow: var(--shadow-lg);
border-color: var(--border-strong);
}
.feature-grid li::marker {
content: "";
}
.feature-grid .feature-title {
display: block;
font-weight: 700;
font-size: 15.5px;
margin-bottom: 4px;
color: var(--text);
}
.feature-grid .feature-icon {
display: inline-block;
margin-right: 6px;
}
.feature-grid .feature-body {
font-size: 14px;
color: var(--text-muted);
line-height: 1.5;
}
.cta-row {
display: flex;
flex-wrap: wrap;
gap: 10px;
margin: 1.5em 0 2em;
}
.btn {
display: inline-flex;
align-items: center;
gap: 8px;
padding: 10px 18px;
font-family: var(--font-sans);
font-weight: 700;
font-size: 15px;
border-radius: var(--radius-sm);
border: 1px solid transparent;
border-bottom: 1px solid transparent !important;
cursor: pointer;
transition: transform 0.08s ease, box-shadow 0.15s ease,
background 0.15s ease, color 0.15s ease;
}
.btn-primary {
background: var(--accent);
color: var(--accent-contrast);
}
.btn-primary:hover {
background: var(--accent-hover);
color: var(--accent-contrast);
transform: translateY(-1px);
box-shadow: var(--shadow);
}
.btn-secondary {
background: transparent;
color: var(--text);
border-color: var(--border-strong) !important;
}
.btn-secondary:hover {
background: var(--bg-elev);
color: var(--text);
transform: translateY(-1px);
box-shadow: var(--shadow);
}
.version-grid {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(110px, 1fr));
gap: 8px;
padding: 0;
list-style: none;
margin: 1em 0 2em;
}
.version-grid li {
margin: 0;
}
.version-grid li::marker {
content: "";
}
.version-grid a {
display: block;
padding: 8px 10px;
text-align: center;
font-family: var(--font-mono);
font-size: 13px;
color: var(--text-muted);
background: var(--bg-elev);
border: 1px solid var(--border);
border-radius: var(--radius-sm);
border-bottom: 1px solid var(--border);
}
.version-grid a:hover {
color: var(--accent);
border-color: var(--accent);
background: var(--accent-soft);
}
.version-grid .current a {
color: var(--accent);
border-color: var(--accent);
font-weight: 700;
background: var(--accent-soft);
}
.book-promo {
display: flex;
gap: 20px;
align-items: center;
padding: 20px;
background: var(--bg-elev);
border: 1px solid var(--border);
border-radius: var(--radius);
margin: 1.5em 0;
box-shadow: var(--shadow);
}
.book-promo img {
width: 110px;
flex-shrink: 0;
border-radius: 4px;
}
.book-promo .book-body {
flex: 1;
}
.book-promo h4 {
margin: 0 0 4px !important;
font-size: 1.05rem !important;
}
.book-promo p {
margin: 4px 0 !important;
font-size: 14px;
color: var(--text-muted);
}
/* ---------- Cover page ---------- */
section.cover {
/* Hidden by default — docsify adds .show only on the homepage route. */
display: none;
position: relative;
min-height: 100dvh;
align-items: center;
justify-content: center;
padding: 40px 24px;
margin-left: var(--sidebar-width);
/* !important overrides the inline gradient docsify injects on
the cover section. */
background:
radial-gradient(
ellipse at 85% 15%,
var(--accent-soft),
transparent 45%
),
radial-gradient(
ellipse at 15% 95%,
rgba(255, 165, 100, 0.08),
transparent 45%
),
var(--bg) !important;
}
section.cover.show {
display: flex;
}
section.cover .cover-main {
max-width: 760px;
text-align: center;
}
section.cover .cover-main > p:last-child a {
display: inline-flex;
align-items: center;
gap: 8px;
margin: 8px 6px 0;
padding: 12px 22px;
font-weight: 700;
font-size: 16px;
border-radius: var(--radius-sm);
border: 1px solid var(--border-strong);
color: var(--text);
background: var(--bg-elev);
box-shadow: var(--shadow);
border-bottom: 1px solid var(--border-strong);
transition: transform 0.1s ease, box-shadow 0.15s ease;
}
section.cover .cover-main > p:last-child a:hover {
transform: translateY(-2px);
box-shadow: var(--shadow-lg);
}
section.cover .cover-main > p:last-child a:first-child {
background: var(--accent);
color: var(--accent-contrast);
border-color: var(--accent);
}
section.cover .cover-main > p:last-child a:first-child:hover {
background: var(--accent-hover);
border-color: var(--accent-hover);
color: var(--accent-contrast);
}
section.cover h1 {
margin: 0 0 0.15em;
font-size: 4rem;
letter-spacing: -0.03em;
font-weight: 700;
line-height: 1;
}
section.cover h1 a {
color: var(--accent);
border-bottom: 0;
position: relative;
display: inline-block;
/* Halo ensures edges stay legible against the gradient backdrop */
text-shadow:
0 0 1px var(--bg),
0 0 12px var(--bg),
0 2px 24px rgba(0, 0, 0, 0.08);
}
section.cover h1 small {
/* Absolute so it hangs off the right edge without affecting the
centering of "Taffy". line-height:1 on both ensures bottom:0
== baseline alignment in the same font family. */
position: absolute;
left: calc(100% + 0.15em);
bottom: 0;
line-height: 1;
font-size: 1rem;
color: var(--text-subtle);
font-weight: 400;
letter-spacing: 0.04em;
text-transform: uppercase;
white-space: nowrap;
text-shadow: none;
}
section.cover blockquote {
margin: 1em 0 1.5em;
padding: 0;
border: 0;
background: transparent;
}
section.cover blockquote p {
font-size: 1.35rem;
color: var(--text-muted);
font-weight: 400;
line-height: 1.45;
margin: 0;
}
section.cover .cover-main > p:nth-last-child(2) {
font-size: 14px;
color: var(--text-subtle);
margin-bottom: 1.5em;
}
.cover-main img {
max-width: 160px;
margin-bottom: 1em;
}
/* ---------- Pagination (next/prev) ---------- */
.docsify-pagination-container {
max-width: var(--content-width);
margin: 0 auto;
padding: 0 40px 80px;
}
.pagination-item {
border: 1px solid var(--border);
border-radius: var(--radius);
background: var(--bg-elev);
}
.pagination-item-title {
color: var(--accent) !important;
}
/* ---------- Responsive ---------- */
@media (max-width: 900px) {
:root {
--sidebar-width: 260px;
}
.sidebar {
transform: translateX(-100%);
box-shadow: var(--shadow-lg);
}
body.close .sidebar {
transform: translateX(0);
}
.content {
margin-left: 0;
}
.sidebar-toggle {
display: flex;
}
.markdown-section {
padding: 72px 22px 80px;
}
.docsify-pagination-container {
padding: 0 22px 80px;
}
section.cover {
margin-left: 0;
}
section.cover h1 {
font-size: 3rem;
}
.book-promo {
flex-direction: column;
text-align: center;
}
}
/* Docsify loading state */
.app-name-link img.logo {
display: none;
}
/* ---------- GitHub corner ---------- */
.github-corner {
position: fixed;
top: 0;
right: 0;
z-index: 20;
border-bottom: 0;
text-decoration: none;
}
.github-corner svg {
width: 72px;
height: 72px;
color: var(--bg);
fill: var(--accent);
}
.github-corner:hover .octo-arm {
animation: octocat-wave 560ms ease-in-out;
}
@keyframes octocat-wave {
0%, 100% { transform: rotate(0); }
20%, 60% { transform: rotate(-25deg); }
40%, 80% { transform: rotate(10deg); }
}
@media (max-width: 900px) {
.github-corner svg {
width: 56px;
height: 56px;
}
.github-corner:hover .octo-arm {
animation: none;
}
.github-corner .octo-arm {
animation: octocat-wave 560ms ease-in-out;
}
}
</style>
</head>
<body>
<div id="app"></div>
<aside
class="book"
style="position: absolute; top: 50px; right: 20px; width: 160px"
>
<br />Need to better understand REST fundamentals? <br /><a
href="http://www.restassuredbook.com"
>
<img
src="assets/book-3d.png"
width="100"
alt="Picture of book cover art for Adam's book: REST Assured, A Pragmatic Approach to API Design"
/>
<br />I wrote a book for you.
</a>
</aside>
<div id="app">Loading…</div>
<script>
window.$docsify = {
name: "Taffy Docs",
name: "Taffy",
repo: "atuttle/Taffy",
maxLevel: 4,
loadSidebar: false,
coverpage: true,
onlyCover: false,
maxLevel: 3,
subMaxLevel: 2,
auto2top: true,
search: {
maxAge: 86400000,
paths: "auto",
placeholder: "Search docs…",
noData: "No results.",
depth: 4,
},
};
</script>
<script src="//cdn.jsdelivr.net/npm/docsify@4"></script>
<script src="//cdn.jsdelivr.net/npm/docsify/lib/plugins/search.min.js"></script>
<script src="//cdn.jsdelivr.net/npm/docsify/lib/plugins/emoji.min.js"></script>
<script src="//cdn.jsdelivr.net/npm/prismjs@1/components/prism-markup-templating.min.js"></script>
<script src="//cdn.jsdelivr.net/npm/prismjs@1/components/prism-javascript.min.js"></script>
<script src="//cdn.jsdelivr.net/npm/prismjs@1/components/prism-bash.min.js"></script>
<script src="//cdn.jsdelivr.net/npm/prismjs@1/components/prism-json.min.js"></script>
<script src="//cdn.jsdelivr.net/npm/prismjs@1/components/prism-cfscript.min.js"></script>
</body>
</html>
+136 -22
View File
@@ -1,32 +1,146 @@
<img alt="Taffy Logo" src="https://taffy.io/images/logo.png" style="max-width: 300px" />
<p class="hero-badges">
<a class="badge" href="https://github.com/atuttle/Taffy"><span>★</span> GitHub</a>
<a class="badge" href="https://github.com/atuttle/Taffy/releases"><span>⬇</span> Releases</a>
<a class="badge" href="https://cfml-slack.herokuapp.com/"><span>💬</span> #taffy on CFML Slack</a>
<a class="badge" href="https://github.com/atuttle/Taffy/blob/master/LICENSE"><span>⚖</span> Apache-2.0</a>
</p>
# Taffy
# Write REST APIs in CFML — without the boilerplate.
The REST Web Service framework for ColdFusion and Lucee
**Taffy** is a full-featured REST framework for ColdFusion and Lucee. Drop a
CFC into your `/resources/` folder, add a URI annotation, and you have an API.
Content negotiation, caching hooks, CORS, JSONP, an interactive dashboard,
OpenAPI JSON, and custom serializers — all included.
- [Download Taffy 3.3.0.zip](https://github.com/atuttle/Taffy/archive/v3.3.0.zip)
- [GitHub](https://github.com/atuttle/Taffy)
- Join us in the **#taffy** chat room on the [CFML Slack](https://cfml-slack.herokuapp.com/)
<p class="cta-row">
<a class="btn btn-primary" href="https://github.com/atuttle/Taffy/archive/v4.0.0.zip">Download v4.0.0</a>
<a class="btn btn-secondary" href="#/?id=quickstart-your-first-api">Quickstart &rarr;</a>
<a class="btn btn-secondary" href="#/4.0.0">Read the Docs</a>
</p>
# Documentation
## Why Taffy?
Nobody has time to keep every framework and every dependency up to date. Sometimes you want to reference the old docs! That's why Taffy tries really hard to maintain our old docs and carry them forward with us. Below is a list of all version-specific Taffy docs releases.
<ul class="feature-grid">
<li>
<span class="feature-title"><span class="feature-icon">⚡</span>One-file APIs</span>
<span class="feature-body">A working API fits in a tweet. Seriously — inheritance and convention do the heavy lifting.</span>
</li>
<li>
<span class="feature-title"><span class="feature-icon">🧭</span>Convention over config</span>
<span class="feature-body">URI lives on the CFC. HTTP verbs map to CFC methods. That's the contract.</span>
</li>
<li>
<span class="feature-title"><span class="feature-icon">🎛</span>Built-in dashboard</span>
<span class="feature-body">Interactive API explorer at your index.cfm. Try every endpoint from a browser without writing a client.</span>
</li>
<li>
<span class="feature-title"><span class="feature-icon">🧬</span>Content negotiation</span>
<span class="feature-body">JSON, XML, JSONP, custom serializers — chosen by <code>Accept</code> header or extension. You pick the defaults.</span>
</li>
<li>
<span class="feature-title"><span class="feature-icon">📘</span>OpenAPI out of the box</span>
<span class="feature-body">Auto-generated OpenAPI/Swagger JSON for every route, new in v4. Point your favorite tooling at it and go.</span>
</li>
<li>
<span class="feature-title"><span class="feature-icon">🧱</span>Pluggable everything</span>
<span class="feature-body">Bring your own bean factory, serializer, status reporter, or caching strategy. The hooks are there.</span>
</li>
</ul>
## Quickstart: Your First API
Three files. That's the whole API.
**Application.cfc**
```cfscript
component extends="taffy.core.api" {}
```
**index.cfm**
```
<!-- intentionally empty -->
```
**/resources/hello.cfc**
```cfscript
component extends="taffy.core.resource" taffy_uri="/hello" {
function get(){
return rep( [ "hello", "world" ] );
}
}
```
Point a browser at `index.cfm` and you get the Taffy dashboard. Hit
`/hello` and you get JSON. Add a `post()` method — that's your POST handler.
Add `taffy_uri="/hello/{name}"` — now `name` is an argument.
[Read the full Getting Started guide &rarr;](https://github.com/atuttle/Taffy/wiki/Getting-Started)
## Current Release
> **Taffy 4.0.0** is the latest stable release.
> [Release notes](https://github.com/atuttle/Taffy/releases/tag/v4.0.0) ·
> [Download](https://github.com/atuttle/Taffy/archive/v4.0.0.zip) ·
> [Docs](4.0.0.md)
## All Versions
Taffy keeps every version's docs online forever — because nobody has time to
keep every framework and every dependency up to date. If you're still on an
older version, the docs that shipped with it still work.
<!--new_docs_links_here-->
- [v3.5.0](3.5.0.md)
- [v3.4.0](3.4.0.md)
- [v3.3.0](3.3.0.md)
- [v3.2.0](3.2.0.md)
- [v3.1.0](3.1.0.md)
- [v3.0.0](3.0.0.md)
- [v2.2.4](2.2.4.md)
- [v2.2.3](2.2.3.md)
- [v2.2.0](2.2.0.md)
- [v2.1.0](2.1.0.md)
- [v2.0.1](2.0.1.md)
- [v2.0.0](2.0.0.md)
### Taffy 4.x
# Other Resources
<ul class="version-grid">
<li class="current"><a href="#/4.0.0">v4.0.0</a></li>
</ul>
In 2012 I presented a comparison of REST frameworks for CFML at the cf.Objective() conference. [This is all of the source code I used to make that comparison, as well as my slides from the talk.](https://github.com/atuttle/CF-REST-Comparison#coldfusion-rest-comparison)
### Taffy 3.x
<ul class="version-grid">
<li><a href="#/3.7.1">v3.7.1</a></li>
<li><a href="#/3.7.0">v3.7.0</a></li>
<li><a href="#/3.6.0">v3.6.0</a></li>
<li><a href="#/3.5.0">v3.5.0</a></li>
<li><a href="#/3.4.0">v3.4.0</a></li>
<li><a href="#/3.3.0">v3.3.0</a></li>
<li><a href="#/3.2.0">v3.2.0</a></li>
<li><a href="#/3.1.0">v3.1.0</a></li>
<li><a href="#/3.0.0">v3.0.0</a></li>
</ul>
### Taffy 2.x
<ul class="version-grid">
<li><a href="#/2.2.4">v2.2.4</a></li>
<li><a href="#/2.2.3">v2.2.3</a></li>
<li><a href="#/2.2.0">v2.2.0</a></li>
<li><a href="#/2.1.0">v2.1.0</a></li>
<li><a href="#/2.0.1">v2.0.1</a></li>
<li><a href="#/2.0.0">v2.0.0</a></li>
</ul>
## Community
- 💬 Join **#taffy** on the [CFML Slack](https://cfml-slack.net/)
- 🐛 [Report issues on GitHub](https://github.com/atuttle/Taffy/issues)
- 🙋 [Contribute](https://github.com/atuttle/Taffy/blob/master/CONTRIBUTING.md)
- 📜 [Changelog](https://github.com/atuttle/Taffy/releases)
## Other Resources
<div class="book-promo">
<a href="http://www.restassuredbook.com"><img src="assets/book-3d.png" alt="REST Assured — A Pragmatic Approach to API Design" /></a>
<div class="book-body">
<h4>Need to level up on REST fundamentals?</h4>
<p>Taffy's author wrote a book on API design. <em>REST Assured — A Pragmatic Approach to API Design</em> covers the parts every framework leaves out.</p>
<p><a href="http://www.restassuredbook.com">Get the book &rarr;</a></p>
</div>
</div>