Using URL Paths in OpenWhisk Web Actions

This post is more than 2 years old.

Time for another quick OpenWhisk tip. As you know (or may know!), when you create an OpenWhisk web action, you can pass parameters via the query string or via a form post. So consider the following trivial action:

function main(args) {

    if(!args.name) args.name = 'Nameless';
	let result = {
		string:'Hello '+args.name
	}

	return {
		headers: { 
			'Access-Control-Allow-Origin':'*',
			'Content-Type':'application/json'
		}, 
		statusCode:200,
		body: new Buffer(JSON.stringify(result)).toString('base64')
	}

}

All this action does is say hello to a name that comes from the arguments passed to the function. After creating the action (and enabling web support), you can then hit it at your URL like so:

https://openwhisk.ng.bluemix.net/api/v1/web/
rcamden@us.ibm.com_My%20Space/safeToDelete/pathTest

And pass a name like so:

https://openwhisk.ng.bluemix.net/api/v1/web/
rcamden@us.ibm.com_My%20Space/safeToDelete/pathTest?name=Raymond+Camden

Cool. But what if you want to use the URL path instead of query parameters? Perhaps something like so:

https://openwhisk.ng.bluemix.net/api/v1/web/
rcamden@us.ibm.com_My%20Space/safeToDelete/pathTest/name/Raymond+Camden

The good news is that this is pretty simple to support. OpenWhish will pass this information to your action as args.__ow_path. OpenWhisk actually passes a bunch of things you can read about here, but for our purposes, the __ow_path value is all we need. So consider this new version:

function main(args) {

	if(args.__ow_path && args.__ow_path.length) {
		/*
		support /name/X only
		*/
		let parts = args.__ow_path.split('/');
		if(parts.length === 3 && parts[1].toLowerCase() === 'name') {
			args.name = decodeURIComponent(parts[2]);
		}
	}

    if(!args.name) args.name = 'Nameless';
	let result = {
		string:'Hello '+args.name
	}


	return {
		headers: { 
			'Access-Control-Allow-Origin':'*',
			'Content-Type':'application/json'
		}, 
		statusCode:200,
		body: new Buffer(JSON.stringify(result)).toString('base64')
	}

}

All I've done is look for the path, see if it has length, and then I parse it. Now - in my particular case I'm assuming only one valid path: /name/X. Obviously you could write the code to be a bit more generic, perhaps in the form of: /name/value/name/value etc. But to keep it simple I just look for /name/X and if that matches, set args.name to it. The result works perfectly:

This is fine

Heh, oops. Almost. So you may have noticed I used decodeURIComponent above, and it works correctly if you encode spaces with %20:

https://openwhisk.ng.bluemix.net/api/v1/web/
rcamden@us.ibm.com_My%20Space/safeToDelete/pathTest/name/Raymond%20Camden

This is ok

So from what I've seen in my research, the plus sign is not meant to be decoded, and it may actually be part of the original string. So what you do here is up to you really. In this particular use case where I'm working with names, it would probably be safe to go ahead and replace plus signs with spaces:

args.name = decodeURIComponent(parts[2]).replace(/\+/g, ' ');

I hope this helps! As a quick aside, the URL in my tests includes safeToDelete. That has nothing to do with the post. I'm just trying to use that package as a way to flag to myself actions I can safely delete later. (As you can imagine, I've got a bunch of crap up now on OpenWhisk and I'm starting to feel like I need to clean up a bit!)

Raymond Camden's Picture

About Raymond Camden

Raymond is a senior developer evangelist for Adobe. He focuses on document services, JavaScript, and enterprise cat demos. If you like this article, please consider visiting my Amazon Wishlist or donating via PayPal to show your support. You can even buy me a coffee!

Lafayette, LA https://www.raymondcamden.com

Archived Comments

Comment 1 by Simon Berman posted on 8/15/2017 at 6:13 AM

Hey Mate,

Thanks for the tips. How do you define the API gateway that calls this though?

Comment 2 (In reply to #1) by Raymond Camden posted on 8/15/2017 at 1:29 PM

I haven't tried it with the API Management feature yet. Is that what you meant - and have you tried it yourself yet to see what happens? It may "just work".

Comment 3 (In reply to #2) by Simon Berman posted on 8/16/2017 at 12:47 AM

I gave it a go but cannot figure out how to do wildcards, so it seems you'd need to register every single variable path, clearly a blocker. Tried with path/{variable} which is pretty standard and works with AWS, but no. I think i'll have to use query string params, but that effectively means I'll need my services to handle multiple cases e.g. GET api/file?fileId=1234 would be used to get all files, and a specific file if an id is passed. Otherwise I'll have to do things like GET api/file/fileId?id=1234 which I find inelegant. I'm gonna go with option one for now I think as it will also keep the services a bit warmer.

Comment 4 (In reply to #3) by Raymond Camden posted on 8/16/2017 at 12:45 PM

Hmm. So to be clear, you defined an API Gateway and it gave you a url, lets say http://foo. Are you saying you can't use http://foo/zoo, ie just add /SOMETHING to the end?

Comment 5 (In reply to #4) by Simon Berman posted on 9/22/2017 at 2:58 AM

Hey Mate, sorry for the delay. The problem is I can't put a dynamic value at the end, say an ID because there is no way to define a wildcard that I can see in API gateway. I'd have to explicitly map a URL for every ID, imagine the size of the swagger file!!!

Comment 6 (In reply to #5) by Raymond Camden posted on 9/22/2017 at 10:57 AM

Swagger does support dynamic values though. I'm definitely not a Swagger/OpenAPI expert, but I know that's supported. Something like {ID} or some such. Are you using the API gateway that is free and with OpenWhisk, or the "real" API Connect?

Comment 7 (In reply to #6) by Simon Berman posted on 9/25/2017 at 8:07 PM

Yeah, swagger does but API gateway does not it seems. Would using API connect give me this functionality do you think?

Comment 8 (In reply to #7) by Raymond Camden posted on 9/26/2017 at 7:45 PM

I should know this and I don't - so I apologize - but you can try API Connect for free - so it's a risk free test. Can you give it a shot and report back?