Skip to main content
Ben Nadel at cf.Objective() 2014 (Bloomington, MN) with: Jeff McDowell
Ben Nadel at cf.Objective() 2014 (Bloomington, MN) with: Jeff McDowell ( @jeff_s_mcdowell )

Spreading Http And HttpParam Tags Across Multiple Function Calls In Lucee CFML 5.3.8.201

By
Published in Comments (3)

Back when InVision was running on Adobe ColdFusion 10, I leaned heavily on the native Http.cfc component when making network requests. However, once we switched over to Lucee CFML 5.3, where tag support in CFScript is much more robust, I found myself reverting back to the http and httpParam tags for network requests. There's something I find so elegant about the syntax of tags and their builder-like pattern of construction. One nice about using the Http.cfc ColdFusion component, however, is that it can be passed around and augmented such as with the injection of tracing headers. This got me thinking about augmenting http tags with httpParam tags; and, whether such an augmentation could be spread across function calls in Lucee CFML 5.3.8.201.

This isn't the first time that I've considered such a pattern. Last year, I demonstrated that you could compose a SQL statement along with its cfqueryparam tags across multiple function calls. At the time, I didn't know if that was something specific to the cfquery tags; or, if that was a technique that would generally work with all tags in Lucee CFML.

To explore this concept, I'm going to create a page that composes httpParam tags inside Function calls. Then, I'm going to make an HTTP request to an end-point that echoes the incoming HTTP headers in its response. First, let's just take a look at the target.cfm template - our HTTP end-point:

<cfscript>

	// Extract inbound HTTP headers.
	incomingHeaders = getHttpRequestData( false ).headers;

	// Prepare HTTP headers to be echoed in BODY of response.
	serializedHeaders = serializeJson( incomingHeaders );
	responsePayload = charsetDecode( serializedHeaders, "utf8" );

	content
		type = "application/json; charset=utf8"
		variable = responsePayload
	;

</cfscript>

As you can see, this simply grabs the .headers property out of the request context, serializes it as JSON (JavaScript Object Notation), and dumps it in to the response body.

Now, we can go about generating that request using the http tag and several httpParam tags. Note that two of the httpParam tags are defined in UDFs (User Defined Functions):

<cfscript>

	// Construct the root URL that we are going to hit (sibling to this template).
	host = "http://#cgi.server_name#:#cgi.server_port#";
	wwwRoot = getDirectoryFromPath( cgi.script_name );
	targetUrl = ( host & wwwRoot & "target.cfm" );

	// The "target.cfm" end-point does nothing but serialize and return the incoming HTTP
	// HEADERS as a JSON (JavaScript Object Notation) payload. Our goal here is to see if
	// we can compose that set of HTTP headers across a series of Function calls.
	http
		result = "targetResponse"
		method = "get"
		url = targetUrl
		{

		// Gather "httpParam" TAGS across external function calls.
		injectAuthorizationHeaders();
		injectTracingHeaders();

		// Mix-in our own "local" header.
		httpParam
			type = "header"
			name = "X-Meep"
			value = "moop"
		;
	}

	// Output the HTTP HEADERS that the target end-point found and returned.
	dump( deserializeJson( targetResponse.fileContent ) );

	// ------------------------------------------------------------------------------- //
	// ------------------------------------------------------------------------------- //

	/**
	* I apply the Authorization HTTP header to the current HTTP context.
	*/
	public void function injectAuthorizationHeaders() {

		httpParam
			type = "header"
			name = "Authorization"
			value = "Bearer: ICANHASCHEEZEBURGER"
		;

	}


	/**
	* I apply the tracing HTTP headers to the current HTTP context.
	*/
	public void function injectTracingHeaders() {

		param
			name = "request.requestID"
			type = "string"
			default = "request-#createUuid()#"
		;

		httpParam
			type = "header"
			name = "X-Request-ID"
			value = request.requestID
		;

	}

</cfscript>

As you can see, inside our http tag context, we're calling two methods:

  • injectAuthorizationHeaders()
  • injectTracingHeaders()

Each of these methods contains an httpParam tag that magically attaches to and augments the http context in current the call-stack. And, when we run this ColdFusion template, we get the following output:

HTTP headers echoed in a ColdFusion API response.

As you can see, all three httpParam header values were attached to the outgoing HTTP request. This demonstrates that http and httpParam tags can be aggregated across several method calls. This means that we can easily create functions that apply "behaviors" (such as Authorization and Tracing) to any contextual http tag in our application.

Freaking Lucee CFML - it's just awesome!

Want to use code from this post? Check out the license.

Reader Comments

15,728 Comments

@Gavin,

That looks pretty cool. I feel like I've heard you all talk about that on the Modernize or Die show. Looks like it has a lot of flexibility. And, the ability to set defaults across the board is very nice. 💪

15,728 Comments

Ugggg, so, I was just trying this technique in Adobe ColdFusion and it throws the error:

Context validation error for tag cfhttpparam.

The tag must be nested inside a cfhttp tag.

Super lame! This is true in both ACF 2021 and 2023. It doesn't make sense - the whole base-tag context mechanisms are (almost) the same in the two runtimes. I'm not sure why Adobe ColdFusion is struggling with this.

Post A Comment — I'd Love To Hear From You!

Post a Comment

I believe in love. I believe in compassion. I believe in human rights. I believe that we can afford to give more of these gifts to the world around us because it costs us nothing to be decent and kind and understanding. And, I want you to know that when you land on this site, you are accepted for who you are, no matter how you identify, what truths you live, or whatever kind of goofy shit makes you feel alive! Rock on with your bad self!
Ben Nadel