Snurl.com (SnipUrl) API ColdFusion Component Wrapper

Posted February 5, 2009 at 12:32 PM by Ben Nadel

Tags: ColdFusion

I was thinking of integrating my Snurl.com account (one of the many url shortening services) into KinkyTwits, my ColdFusion and jQuery powered Twitter client. Before I could do that, though, I had to create a ColdFusion component that would wrap around the SnipURL API provided by Snurl.com. The API itself has Create, Update, and Read style methods, but I only included the Create and Read wrappers as I don't see a real need to update short URLs.

Although I did run into some oddities with the UrlDecode() method this morning, the SnipURL.cfc ColdFusion component is rather simple:

  • <cfcomponent
  • output="false"
  • hint="I provide SnipURL API functionality.">
  •  
  •  
  • <cffunction
  • name="Init"
  • access="public"
  • returntype="any"
  • output="false"
  • hint="I return an initialized component.">
  •  
  • <!--- Define arguments. --->
  • <cfargument
  • name="Username"
  • type="string"
  • required="true"
  • hint="I am the username for the SnipURL account."
  • />
  •  
  • <cfargument
  • name="APIKey"
  • type="string"
  • required="true"
  • hint="I am the API key gotten from the SnipURL account settings."
  • />
  •  
  • <!--- Set up instance variables and store arguments. --->
  • <cfset VARIABLES.Instance = {
  • Username = ARGUMENTS.Username,
  • APIKey = ARGUMENTS.APIKey
  • } />
  •  
  • <!--- Return This reference. --->
  • <cfreturn THIS />
  • </cffunction>
  •  
  •  
  • <cffunction
  • name="CreateSnip"
  • access="public"
  • returntype="struct"
  • output="false"
  • hint="I create a snip with the given data.">
  •  
  • <!--- Define arguments. --->
  • <cfargument
  • name="URL"
  • type="string"
  • required="true"
  • hint="I am the URL that is being shortened."
  • />
  •  
  • <cfargument
  • name="NickName"
  • type="string"
  • required="false"
  • default=""
  • hint="I am the nickname that is associated with the short URL (will attempt to be used in the short URL)."
  • />
  •  
  • <cfargument
  • name="Title"
  • type="string"
  • required="false"
  • default=""
  • hint="I am the title of the snip used in the snip details."
  • />
  •  
  • <cfargument
  • name="PrivateKey"
  • type="string"
  • required="false"
  • default=""
  • hint="I am the private key."
  • />
  •  
  • <cfargument
  • name="Owner"
  • type="string"
  • required="false"
  • default=""
  • hint="I am the optional owner (if not by the given account)."
  • />
  •  
  • <!--- Define the local scope. --->
  • <cfset var LOCAL = {} />
  •  
  • <!---
  • Create the parameters collection and set required
  • values for API call.
  • --->
  • <cfset LOCAL.Parameters = {
  • SnipUser = VARIABLES.Instance.Username,
  • SnipAPI = VARIABLES.Instance.APIKey,
  • SnipLink = ARGUMENTS.URL
  • } />
  •  
  • <!--- Check for optional parametesr. --->
  • <cfif Len( ARGUMENTS.NickName )>
  •  
  • <cfset LOCAL.Parameters.SnipNick = ARGUMENTS.NickName />
  •  
  • </cfif>
  •  
  • <cfif Len( ARGUMENTS.Title )>
  •  
  • <cfset LOCAL.Parameters.SnipTitle = ARGUMENTS.Title />
  •  
  • </cfif>
  •  
  • <cfif Len( ARGUMENTS.PrivateKey )>
  •  
  • <cfset LOCAL.Parameters.SnipPK = ARGUMENTS.PrivateKey />
  •  
  • </cfif>
  •  
  • <cfif Len( ARGUMENTS.Owner )>
  •  
  • <cfset LOCAL.Parameters.SnipOwner = ARGUMENTS.Owner />
  •  
  • </cfif>
  •  
  •  
  • <!--- Submit the raw API request. --->
  • <cfset LOCAL.ResponseXML = THIS.SubmitRawSnipRequest(
  • "http://snipr.com/site/getsnip",
  • LOCAL.Parameters
  • ) />
  •  
  •  
  • <!--- Create the response structure. --->
  • <cfset LOCAL.Response = {
  • ID = LOCAL.ResponseXML.XmlRoot.ID.XmlText,
  • Title = LOCAL.ResponseXML.XmlRoot.Title.XmlText,
  • URL = UrlDecode(
  • LOCAL.ResponseXML.XmlRoot.URL.XmlText,
  • "utf-8"
  • ),
  • Created = ParseDateTime( LOCAL.ResponseXML.XmlRoot.Created.XmlText )
  • } />
  •  
  • <!--- Return API response. --->
  • <cfreturn LOCAL.Response />
  • </cffunction>
  •  
  •  
  • <cffunction
  • name="GetSnipDetails"
  • access="public"
  • returntype="struct"
  • output="false"
  • hint="I get details for the snip at the given ID.">
  •  
  • <!--- Define arguments. --->
  • <cfargument
  • name="ID"
  • type="string"
  • required="true"
  • hint="I am the ID of the snip (URL will be stripped automatically if need-be)."
  • />
  •  
  • <!--- Define the local scope. --->
  • <cfset var LOCAL = {} />
  •  
  • <!--- Strip out any leading URL information to get the ID. --->
  • <cfset ARGUMENTS.ID = REReplaceNoCase(
  • ARGUMENTS.ID,
  • "^http://[^/]+/",
  • "",
  • "one"
  • ) />
  •  
  • <!---
  • Create the parameters collection and set required
  • values for API call.
  • --->
  • <cfset LOCAL.Parameters = {
  • SnipUser = VARIABLES.Instance.Username,
  • SnipAPI = VARIABLES.Instance.APIKey,
  • SnipID = ARGUMENTS.ID
  • } />
  •  
  • <!--- Submit the raw API request. --->
  • <cfset LOCAL.ResponseXML = THIS.SubmitRawSnipRequest(
  • "http://snipr.com/site/getsnipdetails",
  • LOCAL.Parameters
  • ) />
  •  
  •  
  • <!--- Create the response structure. --->
  • <cfset LOCAL.Response = {
  • ID = LOCAL.ResponseXML.XmlRoot.ID.XmlText,
  • Title = LOCAL.ResponseXML.XmlRoot.Title.XmlText,
  • URL = UrlDecode( LOCAL.ResponseXML.XmlRoot.URL.XmlText ),
  • Clicks = Val( LOCAL.ResponseXML.XmlRoot.Clicks.XmlText ),
  • Unique = Val( LOCAL.ResponseXML.XmlRoot.Unique.XmlText ),
  • Created = ParseDateTime( LOCAL.ResponseXML.XmlRoot.Created.XmlText ),
  • Modified = ParseDateTime( LOCAL.ResponseXML.XmlRoot.Modified.XmlText )
  • } />
  •  
  • <!--- Return API response. --->
  • <cfreturn LOCAL.Response />
  • </cffunction>
  •  
  •  
  • <cffunction
  • name="SubmitRawSnipRequest"
  • access="public"
  • returntype="xml"
  • output="false"
  • hint="I submit the raw HTTP POST and return the XML document.">
  •  
  • <!--- Define arguments. --->
  • <cfargument
  • name="URL"
  • type="string"
  • required="true"
  • hint="I am the API action URL."
  • />
  •  
  • <cfargument
  • name="Parameters"
  • type="struct"
  • required="true"
  • hint="I am the set of parameters to pass to API."
  • />
  •  
  • <!--- Define the local scope. --->
  • <cfset var LOCAL = {} />
  •  
  • <!--- Make CFHTTP POST request. --->
  • <cfhttp
  • method="post"
  • url="#ARGUMENTS.URL#"
  • useragent="SnipURLAPIBot"
  • result="LOCAL.APIResponse">
  •  
  • <!--- Loop over paramters to post. --->
  • <cfloop
  • item="LOCAL.Parameter"
  • collection="#ARGUMENTS.Parameters#">
  •  
  • <!--- Send through as form field. --->
  • <cfhttpparam
  • type="formfield"
  • name="#LCase( LOCAL.Parameter )#"
  • value="#ARGUMENTS.Parameters[ LOCAL.Parameter ]#"
  • encoded="false"
  • />
  •  
  • </cfloop>
  •  
  • </cfhttp>
  •  
  •  
  • <!--- Check the response for a successful connection. --->
  • <cfif NOT FindNoCase( "200", LOCAL.APIResponse.StatusCode )>
  •  
  • <!--- Throw error. --->
  • <cfthrow
  • type="SnipURL.SubmitRawSnipRequest.HTTPFailure"
  • message="The API request failed - #LOCAL.APIResponse.StatusCode#"
  • detail="The API request failed: #LOCAL.APIResponse.FileContent#"
  • />
  •  
  • </cfif>
  •  
  •  
  • <!--- Parse the resposne into XML. --->
  • <cfset LOCAL.ResponseXML = XmlParse(
  • Trim( LOCAL.APIResponse.FileContent )
  • ) />
  •  
  • <!--- Return XML document. --->
  • <cfreturn LOCAL.ResponseXML />
  • </cffunction>
  •  
  • </cfcomponent>

When you call the SnipURL.cfc methods, it submits the POST request and parses the XML response into easy-to-use ColdFusion structures. Here's a small demo that creates a shortened URL and then retrieves the URL details:

  • <!--- Create the SnipURL API component. --->
  • <cfset objAPI = CreateObject( "component", "SnipURL" ).Init(
  • Username = "XXXXXX",
  • APIKey = "XXXXXXXX"
  • ) />
  •  
  • <!--- Create a snip. --->
  • <cfset objResponse = objAPI.CreateSnip(
  • URL = "http://flickr.com/photos/31062833@N07/3255353692/",
  • NickName = "dangy"
  • ) />
  •  
  • <!--- Output the response. --->
  • <cfdump
  • var="#objResponse#"
  • label="CreateSnip() Response"
  • />
  •  
  •  
  • <!--- Get the snip details. --->
  • <cfset objResponse = objAPI.GetSnipDetails(
  • ID = "dangy"
  • ) />
  •  
  • <!--- Output the response. --->
  • <cfdump
  • var="#objResponse#"
  • label="GetSnipDetails() Response"
  • />

When we run the above code, we get the following CFDump output:

 
 
 
 
 
 
Snurl.com (SnipURL) API ColdFusion Component Wrapper Response Structure. 
 
 
 

Nothing too complicated here, but maybe this will be useful to someone.



Reader Comments

Feb 5, 2009 at 1:05 PM // reply »
92 Comments

Don't forget to look over shrinkURL in case you ever want to access other services: http://snurl.com/anom6


Feb 5, 2009 at 1:08 PM // reply »
11,246 Comments

@Andy,

That is badass. I see it even covers Snurl! Perhaps I will just skip my integration and move to yours directly.


Feb 5, 2009 at 1:21 PM // reply »
92 Comments

Snurl is the service that I personally use. It's quick, been around for a while, and offers built in tracking for URLs. Those are the three main requirements for me personally.

I hope you like it Ben...it was something I came up with so that I could integrate into the blog software that I'm writing. My original goal was to write a blog post then, when published, the software would create a short URL from a selected service, then submit a tweet about it using the Twitter API.

Adam Tuttle over at Fusiongrokker.com has submitted several changes and additions so it's been seen by more than just my eyes. Please let me know if you've got any suggestions. I'm hoping to let this get some good exposure and your software would definitely help it do that.


Feb 6, 2009 at 9:03 AM // reply »
20 Comments

Indeed, it's pretty well tested at this point. And I've submitted some changes to make it compatible with CF 6, and 7, as well as Railo. :)


Feb 6, 2009 at 9:40 AM // reply »
92 Comments

I'll be taking Adam's Railo compatibility changes, adding some additional services like URLZen and Zi.ma, and releasing shrinkURL 0.4 this weekend.


Feb 6, 2009 at 9:43 AM // reply »
11,246 Comments

@Andy,

I haven't looked at the actual code yet - are you building all the services into one CFC? Or is there like an abstract CFC that the different services extends (like Snul.cfc extends ShrinkUrl.cfc)?


Feb 6, 2009 at 10:09 AM // reply »
92 Comments

All services in one CFC.

Currently there's a controller method "shrink" which calls a single method for each service, each of which in turn reference a master "callAPI" method.

There's a structure in the CFC's variables scope containing meta data about each of the services. This metadata can be exposed by calling the listServices method.

I've got plans to remove the unique methods for each service, essentially building a method at runtime based on the metadata for that service.


Feb 6, 2009 at 10:19 AM // reply »
20 Comments

Andy, I'm not sure if I'm misunderstanding your intentions, but wouldn't it be easier just to write a function that uses the configuration from the services structure to compose the API call as needed? You may need to add a configuration point or two (ReturnsXML = true/false, comes to mind), but it doesn't sound too difficult. :)


Feb 6, 2009 at 11:00 AM // reply »
92 Comments

That's pretty much exactly what I'm talking about. One method which uses the metadata from the services structure to make the call.


Post A Comment

Comment Etiquette: Please do not post spam. Please keep the comments on-topic. Please do not post unrelated questions or large chunks of code. And, above all, please be nice to each other - we're trying to have a good conversation here.

Please review the following issues:

Author Name:


Author Email:

Author Website:

Comment:

Supported HTML tags for formatting: <strong>bold</strong>   <em>italic</em>   <code>code</code>







  • Help Wanted - Find Your Next ColdFusion Job
Ben Nadel's Company - Epicenter Consulting Recent Blog Comments
May 23, 2013 at 9:52 PM
Preventing Links In Standalone iPhone Applications From Opening In Mobile Safari
@Muhmmadibn Did you figure out a solution to launching PDFs? I am running into the same issues myself. There is no way to close the PDF or go back once you launch it. Thanks in advance! ... read »
May 23, 2013 at 6:06 PM
The Girl Who Broke My Heart, And Made Me A Better Person
Good day,ladies and gentle men, my name is Dr AMADI the great spell caster in Africa, i have help so many people for different kind of problems,who say there is no solution to problems on earth, that ... read »
May 23, 2013 at 4:26 PM
ColdFusion QueryAppend( qOne, qTwo )
@Heather, Glad people are still getting value out of this! ... read »
May 23, 2013 at 3:49 PM
Strange Interaction Between DeserializeJson(), ArrayContains(), And Database Values In ColdFusion
@WebManWalking, I meant the code at the bottom (not the video). I did try to experiment with an intermediary variable, like: value = users.id[ i ]; arrayContains( userIDs, value ); ... but t ... read »
May 23, 2013 at 11:06 AM
Strange Interaction Between DeserializeJson(), ArrayContains(), And Database Values In ColdFusion
@Ben, Are you talking about As Number: YES As String: YES As Java: YES? If so, that's with 3 different ways of referencing the constant 1, not users.id[1]. Query object references(*) are what seem ... read »
May 23, 2013 at 9:55 AM
Strange Interaction Between DeserializeJson(), ArrayContains(), And Database Values In ColdFusion
@Dan, According to the CF Admin, I'm running Java "1.6.0_45". As far as the DB column, in the database it's an INT. I'll see if I can dig into what CF sees it as. @WebManWalking, But h ... read »
May 23, 2013 at 9:49 AM
Strange Interaction Between DeserializeJson(), ArrayContains(), And Database Values In ColdFusion
@Ben, I think the problem is that we're used to loose typing in ColdFusion, like JavaScript. If a value is a number but it's needed in an expression to be a string, noooo problem. I've encountered ... read »
May 23, 2013 at 9:47 AM
ColdFusion QueryAppend( qOne, qTwo )
You rock! Thank you, thank you, thank you!!! ... read »
InVision App - Prototyping Made Beautiful With Prototyping Tools