{"__v":20,"_id":"550979e6dd77250d007369f2","category":{"__v":16,"_id":"550974cc368a56170041475b","project":"55070e814bb83b2500ec9404","version":"550974cb368a561700414757","pages":["55097808ad1f0523008ecbca","550979624c7c3f2300aabf01","5509797daa9bd525001a0656","550979a0aa9bd525001a0658","550979ba4c7c3f2300aabf03","550979e6dd77250d007369f2","55097a02dd77250d007369f4","55097a0fad1f0523008ecbce","55097a1baa9bd525001a065a","55097a212dd6a11900e6e7b0","55097a28ad1f0523008ecbd0","55097a304c7c3f2300aabf05","55097a3add77250d007369f6","55097a402dd6a11900e6e7b2","55097a6fdd77250d007369f8","55097a77ad1f0523008ecbd2"],"reference":false,"createdAt":"2015-03-18T11:08:22.079Z","from_sync":false,"order":3,"slug":"handling-requests-with-controllers","title":"Handling Requests with Controllers"},"project":"55070e814bb83b2500ec9404","user":"55070d24d30b3f190011b941","version":{"__v":1,"_id":"550974cb368a561700414757","forked_from":"55070e814bb83b2500ec9407","project":"55070e814bb83b2500ec9404","createdAt":"2015-03-18T12:51:23.709Z","releaseDate":"2015-03-18T12:51:23.709Z","categories":["550974cc368a561700414758","550974cc368a561700414759","550974cc368a56170041475a","550974cc368a56170041475b","550974cc368a56170041475c","550974cc368a56170041475d","550974cc368a56170041475e"],"is_hidden":false,"is_beta":false,"is_stable":true,"codename":"","version_clean":"1.4.0","version":"1.4"},"updates":[],"createdAt":"2015-03-18T13:13:10.959Z","link_external":false,"link_url":"","githubsync":"","sync_unique":"","hidden":false,"api":{"results":{"codes":[]},"auth":"required","params":[],"url":""},"order":5,"body":"If you've ever needed to create an XML or JSON API for your Wheels application, \nyou may have needed to go down the path of creating a separate controller or \nseparate actions for the new format. This introduces the need to duplicate model\ncalls or even break them out into a super long list of before filters. With \nthis, your controllers can get pretty hairy pretty fast.\n\nUsing a few CFWheels functions, you can easily respond to requests for HTML, XML, \nJSON, and PDF formats without adding unnecessary bloat to your controllers.\n[block:api-header]\n{\n  \"type\": \"basic\",\n  \"title\": \"Requesting Different Formats\"\n}\n[/block]\nWith CFWheels Provides functionality in place, you can request different formats \nusing the following methods:\n\n1. URL Variable\n1. URL Extension\n1. Request Header\n\nWhich formats you can request is determined by what you configure in the \ncontroller. See the section below on *Responding to Different Formats in the \nController* for more details.\n\n# 1. URL Variable\n\nCFWheels will accept a URL variable called `format`. If you wanted to request the \nXML version of an action, for example, your URL call would look something like \nthis:\n[block:code]\n{\n  \"codes\": [\n    {\n      \"code\": \"http://www.example.com/products?format=xml\",\n      \"language\": \"text\"\n    }\n  ]\n}\n[/block]\nThe same would go for JSON:\n[block:code]\n{\n  \"codes\": [\n    {\n      \"code\": \"http://www.example.com/products?format=json\",\n      \"language\": \"text\"\n    }\n  ]\n}\n[/block]\n# 2. URL Extension\n\nPerhaps a cleaner way is to request the format as a \"file\" extension. Here are \nthe XML and JSON examples, respectively:\n[block:code]\n{\n  \"codes\": [\n    {\n      \"code\": \"http://www.example.com/products.xml\\nhttp://www.example.com/products.json\",\n      \"language\": \"text\"\n    }\n  ]\n}\n[/block]\nThis works similarly to the *URL variable* approach mentioned above in that \nthere will now be a key in the `params` struct set to the `format` requested. \nWith the XML example, there will be a variable at `params.format` with a value \nof `xml`.\n\n# 3. Request Header\nIf you are calling the CFWheels application as a web service, you can also request\na given format via the HTTP `Accept` header.\n\nIf you are consuming the service with another CFWheels application, your \n`<cfhttp>` call would look something like this:\n \nIn this example, we are sending an `Accept` header with the value for the `xml` \nformat.\n[block:code]\n{\n  \"codes\": [\n    {\n      \"code\": \"<cfhttp url=\\\"http://www.example.com/products\\\">\\n    <cfhttpparam type=\\\"header\\\" name=\\\"Accept\\\" value=\\\"application/octet-stream\\\">\\n</cfhttp>\",\n      \"language\": \"text\"\n    }\n  ]\n}\n[/block]\nHere is a list of values that you can grab from [mimeTypes()](doc:mimetypes) with CFWheels out\nof the box.\n\n1. `html`\n1. `xml`\n1. `json`\n1. `csv`\n1. `pdf`\n1. `xls`\n\nYou can use [addFormat()](doc:addformat) to set more types to the appropriate MIME type for \nreference. For example, we could set a Microsoft Word MIME type in \n`config/settings.cfm` like so: \n[block:code]\n{\n  \"codes\": [\n    {\n      \"code\": \"<cfset addFormat(extension=\\\"doc\\\", mimeType=\\\"application/msword\\\")>\",\n      \"language\": \"text\"\n    }\n  ]\n}\n[/block]\n\n[block:api-header]\n{\n  \"type\": \"basic\",\n  \"title\": \"Responding to Different Formats in the Controller\"\n}\n[/block]\nThe fastest way to get going with creating your new API and formats is to call \n[provides()](doc:provides) from within your controller's `init()` method.\n\nTake a look at this example:\n[block:code]\n{\n  \"codes\": [\n    {\n      \"code\": \"<cfcomponent extends=\\\"Controller\\\">\\n\\n\\t<cffunction name=\\\"init\\\">\\n\\t\\t<cfscript>\\n\\t\\t\\tprovides(\\\"html,json,xml\\\");\\n\\t\\t</cfscript>\\n\\t</cffunction>\\n\\n\\t<cffunction name=\\\"index\\\">\\n\\t\\t<cfscript>\\n\\t\\t\\tproducts = model(\\\"product\\\").findAll(order=\\\"title\\\");\\n      renderWith(products);\\n\\t\\t</cfscript>\\n\\t</cffunction>\\n\\n</cfcomponent>\",\n      \"language\": \"text\"\n    }\n  ]\n}\n[/block]\nBy calling the [provides()](doc:provides) function in `init()`, you are instructing the \nCFWheels controller to be ready to provide content in a number of formats. \nPossible choices to add to the list are `html` (which runs by default), `xml`, \n`json`, `csv`, `pdf`, and `xls`.\n\nThis is coupled with a call to [renderwith()](doc:renderwith) in the following actions. \nIn the example above, we are setting a query result of products and passing it \nto [renderwith()](doc:renderwith). By passing our data to this function, CFWheels gives us the \nability to respond to requests for different formats, and it even gives us the \noption to just let CFWheels handle the generation of certain formats \nautomatically.\n\nWhen CFWheels handles this response, it will set the appropriate MIME type in the \n`Content-Type` HTTP header as well.\n\n# Providing the HTML Format\nResponding to requests for the HTML version is the same as you're already used \nto with [Rendering Content](doc:rendering-content). [renderwith()](doc:renderwith) will accept the same arguments\nas [renderPage()](doc:renderpage), and you create just a view template in the `views` folder\nlike normal.\n\n# Automatic Generation of XML and JSON Formats\nIf the requested format is `xml` or `json`, the [renderwith()](doc:renderwith) function will \nautomatically transform the data that you provide it. If you're fine with what \nthe function produces, then you're done!\n\n**Best Practices for Providing JSON**\n\nUnfortunately there have been a lot of JSON related issues in CFML over the years. To avoid as many of these problems as possible we thought we'd outline some best practices for you to follow.\n\nFirst of all, always return data as an array of structs. This is done by using the `returnAs` argument (on [findAll()](doc:findall) for example), like this:\n[block:code]\n{\n  \"codes\": [\n    {\n      \"code\": \"authors = model(\\\"author\\\").findAll(returnAs=\\\"structs\\\");\",\n      \"language\": \"text\"\n    }\n  ]\n}\n[/block]\nThe reason for doing it this way is that it will preserve the case for the struct / JSON keys.\n\nSecondly, make use of CFWheels ability to return the JSON values in a specified type. This is done in the [renderWith()](doc:renderwith) function, like this:\n[block:code]\n{\n  \"codes\": [\n    {\n      \"code\": \"renderWith(data=authors, firstName=\\\"string\\\", booksForSale=\\\"integer\\\");\",\n      \"language\": \"text\"\n    }\n  ]\n}\n[/block]\nWith that in place you can be sure that `firstName` will always be treated as a string (i.e. wrap in double quotes) and `booksForSale` as an integer (i.e. no decimal places) when producing the JSON output. Without this, your CFML engine might guess what the data type is, and it wouldn't always be correct unfortunately.\n\n# Providing Your Own Custom Responses\nIf you need to provide content for another type than `xml` or `json`, or if you \nneed to customize what your CFWheels application generates, you have that option.\n\nIn your controller's corresponding folder in `views`, all you need to do is \nimplement a view file like so:\n[block:parameters]\n{\n  \"data\": {\n    \"h-0\": \"Type\",\n    \"h-1\": \"Example\",\n    \"0-0\": \"html\",\n    \"0-1\": \"views/products/index.cfm\",\n    \"1-0\": \"xml\",\n    \"2-0\": \"json\",\n    \"3-0\": \"csv\",\n    \"4-0\": \"pdf\",\n    \"5-0\": \"xls\",\n    \"1-1\": \"views/products/index.xml.cfm\",\n    \"2-1\": \"views/products/index.json.cfm\",\n    \"3-1\": \"views/products/index.csv.cfm\",\n    \"4-1\": \"views/products/index.pdf.cfm\",\n    \"5-1\": \"views/products/index.xls.cfm\"\n  },\n  \"cols\": 2,\n  \"rows\": 6\n}\n[/block]\nIf you need to implement your own XML- or JSON-based output, the presence of \nyour new custom view file will override the automatic generation that CFWheels \nnormally performs.\n\n**Example: PDF Generation**\n\nIf you need to provide a PDF version of the product catalog, the view file at \n`views/products/index.pdf.cfm` may look something like this:\n[block:code]\n{\n  \"codes\": [\n    {\n      \"code\": \"<cfdocument format=\\\"pdf\\\">\\n    <h1>Products</h1>\\n    <table>\\n        <thead>\\n            <tr>\\n                <th>Name</th>\\n                <th>Description</th>\\n                <th>Price</th>\\n            </tr>\\n        </thead>\\n        <tbody>\\n            #includePartial(products)#\\n        </tbody>\\n    </table>\\n</cfdocument>\",\n      \"language\": \"text\"\n    }\n  ]\n}\n[/block]","excerpt":"CFWheels controllers provide some powerful mechanisms for responding to requests for content in XML, JSON, and other formats. You can build an API with ease using these functions.","slug":"responding-with-multiple-formats","type":"basic","title":"Responding with Multiple Formats"}

Responding with Multiple Formats

CFWheels controllers provide some powerful mechanisms for responding to requests for content in XML, JSON, and other formats. You can build an API with ease using these functions.

If you've ever needed to create an XML or JSON API for your Wheels application, you may have needed to go down the path of creating a separate controller or separate actions for the new format. This introduces the need to duplicate model calls or even break them out into a super long list of before filters. With this, your controllers can get pretty hairy pretty fast. Using a few CFWheels functions, you can easily respond to requests for HTML, XML, JSON, and PDF formats without adding unnecessary bloat to your controllers. [block:api-header] { "type": "basic", "title": "Requesting Different Formats" } [/block] With CFWheels Provides functionality in place, you can request different formats using the following methods: 1. URL Variable 1. URL Extension 1. Request Header Which formats you can request is determined by what you configure in the controller. See the section below on *Responding to Different Formats in the Controller* for more details. # 1. URL Variable CFWheels will accept a URL variable called `format`. If you wanted to request the XML version of an action, for example, your URL call would look something like this: [block:code] { "codes": [ { "code": "http://www.example.com/products?format=xml", "language": "text" } ] } [/block] The same would go for JSON: [block:code] { "codes": [ { "code": "http://www.example.com/products?format=json", "language": "text" } ] } [/block] # 2. URL Extension Perhaps a cleaner way is to request the format as a "file" extension. Here are the XML and JSON examples, respectively: [block:code] { "codes": [ { "code": "http://www.example.com/products.xml\nhttp://www.example.com/products.json", "language": "text" } ] } [/block] This works similarly to the *URL variable* approach mentioned above in that there will now be a key in the `params` struct set to the `format` requested. With the XML example, there will be a variable at `params.format` with a value of `xml`. # 3. Request Header If you are calling the CFWheels application as a web service, you can also request a given format via the HTTP `Accept` header. If you are consuming the service with another CFWheels application, your `<cfhttp>` call would look something like this: In this example, we are sending an `Accept` header with the value for the `xml` format. [block:code] { "codes": [ { "code": "<cfhttp url=\"http://www.example.com/products\">\n <cfhttpparam type=\"header\" name=\"Accept\" value=\"application/octet-stream\">\n</cfhttp>", "language": "text" } ] } [/block] Here is a list of values that you can grab from [mimeTypes()](doc:mimetypes) with CFWheels out of the box. 1. `html` 1. `xml` 1. `json` 1. `csv` 1. `pdf` 1. `xls` You can use [addFormat()](doc:addformat) to set more types to the appropriate MIME type for reference. For example, we could set a Microsoft Word MIME type in `config/settings.cfm` like so: [block:code] { "codes": [ { "code": "<cfset addFormat(extension=\"doc\", mimeType=\"application/msword\")>", "language": "text" } ] } [/block] [block:api-header] { "type": "basic", "title": "Responding to Different Formats in the Controller" } [/block] The fastest way to get going with creating your new API and formats is to call [provides()](doc:provides) from within your controller's `init()` method. Take a look at this example: [block:code] { "codes": [ { "code": "<cfcomponent extends=\"Controller\">\n\n\t<cffunction name=\"init\">\n\t\t<cfscript>\n\t\t\tprovides(\"html,json,xml\");\n\t\t</cfscript>\n\t</cffunction>\n\n\t<cffunction name=\"index\">\n\t\t<cfscript>\n\t\t\tproducts = model(\"product\").findAll(order=\"title\");\n renderWith(products);\n\t\t</cfscript>\n\t</cffunction>\n\n</cfcomponent>", "language": "text" } ] } [/block] By calling the [provides()](doc:provides) function in `init()`, you are instructing the CFWheels controller to be ready to provide content in a number of formats. Possible choices to add to the list are `html` (which runs by default), `xml`, `json`, `csv`, `pdf`, and `xls`. This is coupled with a call to [renderwith()](doc:renderwith) in the following actions. In the example above, we are setting a query result of products and passing it to [renderwith()](doc:renderwith). By passing our data to this function, CFWheels gives us the ability to respond to requests for different formats, and it even gives us the option to just let CFWheels handle the generation of certain formats automatically. When CFWheels handles this response, it will set the appropriate MIME type in the `Content-Type` HTTP header as well. # Providing the HTML Format Responding to requests for the HTML version is the same as you're already used to with [Rendering Content](doc:rendering-content). [renderwith()](doc:renderwith) will accept the same arguments as [renderPage()](doc:renderpage), and you create just a view template in the `views` folder like normal. # Automatic Generation of XML and JSON Formats If the requested format is `xml` or `json`, the [renderwith()](doc:renderwith) function will automatically transform the data that you provide it. If you're fine with what the function produces, then you're done! **Best Practices for Providing JSON** Unfortunately there have been a lot of JSON related issues in CFML over the years. To avoid as many of these problems as possible we thought we'd outline some best practices for you to follow. First of all, always return data as an array of structs. This is done by using the `returnAs` argument (on [findAll()](doc:findall) for example), like this: [block:code] { "codes": [ { "code": "authors = model(\"author\").findAll(returnAs=\"structs\");", "language": "text" } ] } [/block] The reason for doing it this way is that it will preserve the case for the struct / JSON keys. Secondly, make use of CFWheels ability to return the JSON values in a specified type. This is done in the [renderWith()](doc:renderwith) function, like this: [block:code] { "codes": [ { "code": "renderWith(data=authors, firstName=\"string\", booksForSale=\"integer\");", "language": "text" } ] } [/block] With that in place you can be sure that `firstName` will always be treated as a string (i.e. wrap in double quotes) and `booksForSale` as an integer (i.e. no decimal places) when producing the JSON output. Without this, your CFML engine might guess what the data type is, and it wouldn't always be correct unfortunately. # Providing Your Own Custom Responses If you need to provide content for another type than `xml` or `json`, or if you need to customize what your CFWheels application generates, you have that option. In your controller's corresponding folder in `views`, all you need to do is implement a view file like so: [block:parameters] { "data": { "h-0": "Type", "h-1": "Example", "0-0": "html", "0-1": "views/products/index.cfm", "1-0": "xml", "2-0": "json", "3-0": "csv", "4-0": "pdf", "5-0": "xls", "1-1": "views/products/index.xml.cfm", "2-1": "views/products/index.json.cfm", "3-1": "views/products/index.csv.cfm", "4-1": "views/products/index.pdf.cfm", "5-1": "views/products/index.xls.cfm" }, "cols": 2, "rows": 6 } [/block] If you need to implement your own XML- or JSON-based output, the presence of your new custom view file will override the automatic generation that CFWheels normally performs. **Example: PDF Generation** If you need to provide a PDF version of the product catalog, the view file at `views/products/index.pdf.cfm` may look something like this: [block:code] { "codes": [ { "code": "<cfdocument format=\"pdf\">\n <h1>Products</h1>\n <table>\n <thead>\n <tr>\n <th>Name</th>\n <th>Description</th>\n <th>Price</th>\n </tr>\n </thead>\n <tbody>\n #includePartial(products)#\n </tbody>\n </table>\n</cfdocument>", "language": "text" } ] } [/block]