Search code examples
coldfusionrailo

Parse a specific variable from a url stored as a string with CFML


I want to parse a specific url variable key value from a url stored as a string. It seems that you can use the underlying java library coldfusion.util.HTMLTools under ACF, but I need it to work under Railo as well. Is there another way, or is using a regular expression the best answer?

I'm trying to retrieve the value of the url variable key without the anchor in a url formatted like the following example. http://example.com?key=134324625625435#gid=0


Solution

  • I was posting as a comment on Scott's answer, but it was getting too long, so...

    John wrote:

    Running the following example I end up with the value of 0, but I think it should be the entire key value?

    <cfoutput>
        <cfset theUrl = "https://docs.google.com/spreadsheet/ccc?key=0AthiZNZ73LBndUzRTUkplbmNhYWc##gid=0" />
        <cfset theUrl = listRest(theUrl, "?")>
        <cfloop list="#theUrl#" index="URLPiece" delimiters="&">
            Key: #listFirst(urlPiece, "=")# Value: #listLast(urlPiece, "=")# <br />
        </cfloop>
    </cfoutput>
    

    The reason for failure of that example URL is it contains a page segment (the bit after the hash), which needs to be stripped off before the query string can be parsed.

    It's also important to get the correct variables/values by wrapping the key/value parts in UrlDecode.

    Plus, it is perfectly acceptable to have an equal sign in the value, so ?key== should return = as the value, which means changing the ListLast to a ListRest and setting includeEmptyFields to true.

    Also, if you have a querystring such as ?a&b then the convention is to set the value to either true or empty string - the current code is setting to the key name, which is wrong.

    In summary, here's a function:

    <cffunction name="getParamsFromUrlString" returntype="Struct" output=false >
        <cfargument name="UrlString" type="String" required />
        <cfargument name="Separator" type="String" default="?" />
        <cfargument name="Delimiter" type="String" default="&" />
        <cfargument name="AssignOp"  type="String" default="=" />
        <cfargument name="EmptyVars" type="String" default="" />
    
        <cfset var QueryString = ListRest( ListFirst( Arguments.UrlString , '##' ) , Arguments.Separator ) />
        <cfset var Result = {} />
    
        <cfloop index="local.QueryPiece" list=#QueryString# delimiters="#Arguments.Delimiter#">
    
            <cfif NOT find(Arguments.AssignOp,QueryPiece)>
                <cfset Result[ UrlDecode( QueryPiece ) ] = Arguments.EmptyVars />
            <cfelse>
                <cfset Result[ UrlDecode( ListFirst(QueryPiece,Arguments.AssignOp) ) ]
                    =  UrlDecode( ListRest(QueryPiece,Arguments.AssignOp,true) ) />
            </cfif>
        </cfloop>
    
        <cfreturn Result />
    </cffunction>
    

    It can be used as simply as:

        <cfset theUrl = "https://docs.google.com/spreadsheet/ccc?key=0AthiZNZ73LBndUzRTUkplbmNhYWc##gid=0" />
        <cfset Data = getParamsFromUrlString( theUrl ) />
        <cfdump var=#Data# />
    

    Or it can be used on complicated non-standard URL strings like this:

        <cfset theUrl = "https://somewhere/index.jsp;x:145;y:54;z:1;f;d:%23%23;w:%3B" />
        <cfset Data = getParamsFromUrlString( theUrl , ';' , ';' , ':' , 'true' ) />
        <cfdump var=#Data# />
    

    And (hopefully) everything in between.