Alter PathSuffix in Apigee with/out Load Balancer

on Friday, February 16, 2018

Apigee’s API Gateway is by many measures a proxy server with some really nice bells and whistles attached. But, it’s still a proxy server at it’s core. Which means it should be able to transform an incoming request before it’s sent to the backend/target resource servers. It can do that, but it’s not as easy as you might hope.

With an API Gateway, a common trasnformation would be to remove a version number from a url before sending the request to the backend server. This scenario crops up when the developer of the resource API didn’t design their system with version numbers in mind. The scenario looks like this:

image

So, in this scenario, the Body API Proxy has a BasePath of /body (proxy.basepath). And the PathSuffix would be /v1/wheels?drive=4WD (proxy.pathsuffix). The developer of the resource service didn’t have version built into the url path, and is expecting a url without it.

Without a Load Balancer Configuration

To make this transformation, we are going to need to artificial create the target endpoints url during the flow process. Seeing that the API Gateway is a proxy server, you would think that you would just need overwrite the request or proxy variables, but most of those are actually read only. Here’s what you’ll need to do:

  1. You’ll use the request.uri and proxy.basepath to figure out the full path suffix.
  2. If the path contains a version number (/v1/) then you will …
  3. Set target.copy.pathsuffix to false. (At the moment, you have to use a Javascript Callout. There is a bug with using an AssignMessage Policy).
    1. This must occur in the Target Endpoint flows (most likely the PreFlow). You can’t do this in the Proxy Endpoint, because the target variables haven’t been created yet. So, they aren’t “in scope”.
  4. You’ll then remove the version number (/v1/) to get the “new” path suffix.
  5. And, finally, you will set the target.url to constructed path. (target.url is one of the few read/write variables.)

Target Endpoint with No Load Balancer

image

With a Load Balancer Configuration

Apigee uses a Load Balancer variable in the Target Endpoint configuration to allow for Resource Server DNS hostnames to be dynamic between the environments. Unfortunately, when this is used, the target.url variable is no longer used. And, you need to set target.copy.queryparams to false as well.

To this, you’ll follow the same steps above, but this time you’ll …

  1. And, finally, you will set the target.url to constructed path. (target.url is one of the few read/write variables.)
  2. Set target.copy.queryparams to false.
  3. Set the {newpathsuffix} variable, which will be configured on the Target Endpoint’s Path.

Target Endpoint with Load Balancer

image

Javascript Callout for Target Endpoint PreFlow: (note that variable {newpathsuffix} isn’t needed when no Load Balancer is involved. It’s being used to make both implementations look similar.)

//  parses the original request to remove the version piece ("/v1", etc)
var basepath = context.getVariable("proxy.basepath")
print("basepath: " + basepath);
var uri = context.getVariable("request.uri");
print("uri: " + uri);
var pathsuffix = uri.substring(basepath.length)
var regex = /(.*)\/v[0-9]+\/(.*)/
var found = regex.exec(pathsuffix)
print("found: " + found)
if(found !== null) {
    //  prevents the request to the backend server from using the original "request.pathSuffix"
    //  this is very important!
    //  the original "request.path" will overwrite whatever we do here if this isn't set
    context.setVariable("target.copy.pathsuffix", false)
    
    // remove the "/v1" part
    var newPathSuffix = found[1]
    if(newPathSuffix.length > 0) { newPathSuffix += "/" }
    newPathSuffix += found[2]
    
    print("newPathSuffix: " + newPathSuffix)
    context.setVariable("newpathsuffix", newPathSuffix)
    
    var targetUrl = context.getVariable("target.url")
    print("target url: " + targetUrl)
    if(targetUrl !== null) {
        
        var pathSuffixRegex = /(.*){newpathsuffix}(.*)/
        var pathSuffixFound = pathSuffixRegex.exec(targetUrl)
        print("pathSuffixFound: " + pathSuffixFound)
        
        if(pathSuffixFound !== null) {
            
            var newUrl = pathSuffixFound[1] + newPathSuffix + pathSuffixFound[2]
            print("new url (replace): " + newUrl)
            context.setVariable("target.url", newUrl);
            
        } else {
            var newUrl = targetUrl + newPathSuffix
            print("new url (append): " + newUrl)
            context.setVariable("target.url", newUrl);
            
        }
    } else {
        // using load balancer
        context.setVariable("target.copy.queryparams", "false") // needed on load balancer
        // the load balancer can use the variable substitution on the  innerText
    }
} else {
    print("newpathsuffix: [empty string]")
    context.setVariable("newpathsuffix", "")
}

target.copy.pathsuffix and target.copy.queryparams

So, these are the key variables that make overwriting the target path possible. The creation of these variables probably has good reasoning behind it, but from an outside perspective they seem really odd. Apigee’s internal system allows you to do a variety of alterations and checks through the Proxy and Target Endpoint flows. These flows can alter most things within the system at the time they execute within the pipeline. BUT, the proxy.pathsuffix and proxy.queryparams are (a) readonly and (b) will overwrite any changes you make to the target.url value. They just ignore everything that happened in the pipeline and override it. This behavior seems to conflict with the way the “flow” system was designed.

0 comments:

Post a Comment


Creative Commons License
This site uses Alex Gorbatchev's SyntaxHighlighter, and hosted by herdingcode.com's Jon Galloway.