How to do HTTPProxyConnection -> BasePath case insensitive?

Not applicable

Configured in Proxy Endpoint

<HTTPProxyConnection>

<BasePath>/abc/def</BasePath>

</HTTPProxyConnection>

If I try basepath with upper case letters, e.g. /AbC/Def, I get classification error:

{ "fault":

{ "faultstring": "Classification failed for host: .. url: /AbC/Def",

"detail": { "code": "CLASSIFICATION_FAILED" }

}

}

How to make the basepath case insensitive?

Solved Solved
2 8 3,026
2 ACCEPTED SOLUTIONS

What you can do is - have basepath as - '/',

Then you could create a conditional flow for a path suffix - the condition can accept a regex, so it can be case insensitive [/abc/def] -

http://docs.apigee.com/api-services/reference/conditions-reference

To answer your question - How to make the basepath case insensitive? - I don't think its possible

View solution in original post

@crina.cimpian

I had a similar issue when placing the Apigee layer over our current api. We already had a number of partners hitting our case insensitive api and did not want to make them change their integration when adding the Apigee layer. Additionally, I did not want to lose the built in features Apigee provides by using the suggestion of having one proxy with "/" as a base path.

I was able to set up a number of proxies in Apigee and wrote a workaround to make them all case insensitive.

1. All base paths for our proxies are now set up in lower case.

2. I created a "Master Proxy" which all of our traffic flows through with a base path of "/v1"

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<ProxyEndpoint name="default">
    <Description/>
    <FaultRules/>
    <PreFlow name="PreFlow">
        <Request>
            <Step>
                <Name>Raise-Fault-404</Name>
                <Condition>(request.header.X-STOP-ME-404 = "true")</Condition>
            </Step>
        </Request>
        <Response/>
    </PreFlow>
    <PostFlow name="PostFlow">
        <Request/>
        <Response/>
    </PostFlow>
    <HTTPProxyConnection>
        <BasePath>/v1</BasePath>
        <Properties/>
        <VirtualHost>secure</VirtualHost>
    </HTTPProxyConnection>
    <RouteRule name="default">
        <TargetEndpoint>default</TargetEndpoint>
    </RouteRule>
</ProxyEndpoint>

3. The "Master Proxy" target endpoint uses path chaining with a path of "/" to point to our other local proxies on Apigee

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<TargetEndpoint name="default">
    <Description/>
    <FaultRules/>
    <PreFlow name="PreFlow">
        <Request>
            <Step>
                <Name>JavaScript-2</Name>
            </Step>
        </Request>
        <Response/>
    </PreFlow>
    <PostFlow name="PostFlow">
        <Request/>
        <Response/>
    </PostFlow>
    <Flows/>
    <LocalTargetConnection>
        <Path>/</Path>
    </LocalTargetConnection>
</TargetEndpoint>

4. The "Master Proxy" has a JavaScript policy on the default target PreFlow which turns the requested message path into lowercase. This has been updated provide support for query parameters and to stop infinite loops.

// Need to stop infinite loop, in case someone hits an endpoint that does not exist
var stop = context.targetRequest.headers['X-STOP-ME-404']


if(stop != 'true')
{
    // Since we will be constructing the uri ourselves tell Apigee not to copy over the path suffix
    context.setVariable("target.copy.pathsuffix", false);


    // Need to stop infinite loop, in case someone hits an endpoint that does not exist
    context.targetRequest.headers['X-STOP-ME-404']='true';
    
    // RegEx for unique case sensitive Ids
    var sfIdRegEx = new RegExp("(?=.*[A-Za-z]+)(?=.*[0-9]+)([A-Za-z0-9]{18}|[A-Za-z0-9]{15})");
    var queryParams = context.getVariable("message.querystring")
    var messagePath = context.getVariable("message.path")
    var constructedMessagePath = "";
    var messagePathParts;


    if (messagePath)
    {
        messagePathParts = messagePath.split('/');
        
        for (var i = 0; i <= messagePathParts.length; i++)
        {
            if(messagePathParts[i])
            {
                constructedMessagePath = constructedMessagePath + '/';
                
                // If it is an Id that is case sensitive.
                if(sfIdRegEx.test(messagePathParts[i]))
                {
                    constructedMessagePath = constructedMessagePath + messagePathParts[i]
                }
                else
                {
                    constructedMessagePath = constructedMessagePath + messagePathParts[i].toLowerCase();
                }
            }
        }
    }
    
    if(queryParams)
    {
        queryParams = '?' + queryParams ;
    }
    
    context.setVariable("message.path", constructedMessagePath);
    context.setVariable("Debug", queryParams);
    
    context.setVariable("target.url", context.getVariable("target.url") + constructedMessagePath + queryParams);
}

5. In order to stop recursion when someone passes in a uri that does not hit one of our proxies I added a Raise Fault policy to send back a 404. This was added on the "Master Proxy" endpoint default PreFlow.

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<RaiseFault async="false" continueOnError="false" enabled="true" name="Raise-Fault-404">
    <DisplayName>Raise Fault-404</DisplayName>
    <Properties/>
    <FaultResponse>
        <Set>
            <Headers/>
            <Payload contentType="text/plain"/>
            <StatusCode>404</StatusCode>
            <ReasonPhrase>NotFound</ReasonPhrase>
        </Set>
    </FaultResponse>
    <IgnoreUnresolvedVariables>true</IgnoreUnresolvedVariables>
</RaiseFault>

So now I have a proxy with a base path of /v1/abc/def/* that can be accessed using

/v1/abc/def/003q000000RTElQ

/v1/Abc/dEf/003q000000RTElQ

/v1/ABC/DEF/003q000000RTElQ

/v1/aBc/DeF/003q000000RTElQ

and the above uris will point to proxy /v1/abc/def/003q000000RTElQ

If someone was to pass a url that does not match a proxy in our org we would return a 404

It works for us because it is a temporary solution until we can notify all are partners that we will be going to case sensitive uris and we don't lose any of the analytics .

View solution in original post

8 REPLIES 8

What you can do is - have basepath as - '/',

Then you could create a conditional flow for a path suffix - the condition can accept a regex, so it can be case insensitive [/abc/def] -

http://docs.apigee.com/api-services/reference/conditions-reference

To answer your question - How to make the basepath case insensitive? - I don't think its possible

Thanks for answer.

With the solution basepath '/', I can have only one proxy deployed (2 different proxies cannot have same basepath deployed on same env).

Ok, I'll take the answer as 'not possible' then.

@crina.cimpian

Just wanted to correct you that

2 different proxies with same basepath but different VHOST can be deployed on same env.

So in case you require to have proxy with similar basepath, you can create a separate VHOST for it.

But I did not get your point when you say "With the solution basepath '/', I can have only one proxy deployed"

In my ORG, proxies with basepath "/" with vhost default, basepath "/" with VHOST secure and other basepaths for different proxy are deployed.

So why this question?

Different VHOST means different domain and/or port.

I have a couple of API Proxies, each independent revisions. They need to have same domain with different basepaths, that are case insensitive. E.g. 'Proxy1' on basepath /base1 and 'Proxy2' on basepath /base2. Based on the above suggestion, in order for 'Proxy1', and 'Proxy2' to support case insensitive basepath - /base1, /BASE1, /Base2 etc - then these 2 proxies have to use same basepath '/', which means they have to be same proxy. Not separate proxies that can be deployed independent.

@crina.cimpian

One domain can have different VHOST but they listen to different ports. So the domain name can be same but PORT number should be different. I hope you got this point.

So one Domain can have multiple VHOST.

There are few benefits for keeping Same Domain with Different VHOST Name. The benefits are a seperate thread for discussion. In case you want to know the benefits, do let me know

@crina.cimpian

I had a similar issue when placing the Apigee layer over our current api. We already had a number of partners hitting our case insensitive api and did not want to make them change their integration when adding the Apigee layer. Additionally, I did not want to lose the built in features Apigee provides by using the suggestion of having one proxy with "/" as a base path.

I was able to set up a number of proxies in Apigee and wrote a workaround to make them all case insensitive.

1. All base paths for our proxies are now set up in lower case.

2. I created a "Master Proxy" which all of our traffic flows through with a base path of "/v1"

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<ProxyEndpoint name="default">
    <Description/>
    <FaultRules/>
    <PreFlow name="PreFlow">
        <Request>
            <Step>
                <Name>Raise-Fault-404</Name>
                <Condition>(request.header.X-STOP-ME-404 = "true")</Condition>
            </Step>
        </Request>
        <Response/>
    </PreFlow>
    <PostFlow name="PostFlow">
        <Request/>
        <Response/>
    </PostFlow>
    <HTTPProxyConnection>
        <BasePath>/v1</BasePath>
        <Properties/>
        <VirtualHost>secure</VirtualHost>
    </HTTPProxyConnection>
    <RouteRule name="default">
        <TargetEndpoint>default</TargetEndpoint>
    </RouteRule>
</ProxyEndpoint>

3. The "Master Proxy" target endpoint uses path chaining with a path of "/" to point to our other local proxies on Apigee

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<TargetEndpoint name="default">
    <Description/>
    <FaultRules/>
    <PreFlow name="PreFlow">
        <Request>
            <Step>
                <Name>JavaScript-2</Name>
            </Step>
        </Request>
        <Response/>
    </PreFlow>
    <PostFlow name="PostFlow">
        <Request/>
        <Response/>
    </PostFlow>
    <Flows/>
    <LocalTargetConnection>
        <Path>/</Path>
    </LocalTargetConnection>
</TargetEndpoint>

4. The "Master Proxy" has a JavaScript policy on the default target PreFlow which turns the requested message path into lowercase. This has been updated provide support for query parameters and to stop infinite loops.

// Need to stop infinite loop, in case someone hits an endpoint that does not exist
var stop = context.targetRequest.headers['X-STOP-ME-404']


if(stop != 'true')
{
    // Since we will be constructing the uri ourselves tell Apigee not to copy over the path suffix
    context.setVariable("target.copy.pathsuffix", false);


    // Need to stop infinite loop, in case someone hits an endpoint that does not exist
    context.targetRequest.headers['X-STOP-ME-404']='true';
    
    // RegEx for unique case sensitive Ids
    var sfIdRegEx = new RegExp("(?=.*[A-Za-z]+)(?=.*[0-9]+)([A-Za-z0-9]{18}|[A-Za-z0-9]{15})");
    var queryParams = context.getVariable("message.querystring")
    var messagePath = context.getVariable("message.path")
    var constructedMessagePath = "";
    var messagePathParts;


    if (messagePath)
    {
        messagePathParts = messagePath.split('/');
        
        for (var i = 0; i <= messagePathParts.length; i++)
        {
            if(messagePathParts[i])
            {
                constructedMessagePath = constructedMessagePath + '/';
                
                // If it is an Id that is case sensitive.
                if(sfIdRegEx.test(messagePathParts[i]))
                {
                    constructedMessagePath = constructedMessagePath + messagePathParts[i]
                }
                else
                {
                    constructedMessagePath = constructedMessagePath + messagePathParts[i].toLowerCase();
                }
            }
        }
    }
    
    if(queryParams)
    {
        queryParams = '?' + queryParams ;
    }
    
    context.setVariable("message.path", constructedMessagePath);
    context.setVariable("Debug", queryParams);
    
    context.setVariable("target.url", context.getVariable("target.url") + constructedMessagePath + queryParams);
}

5. In order to stop recursion when someone passes in a uri that does not hit one of our proxies I added a Raise Fault policy to send back a 404. This was added on the "Master Proxy" endpoint default PreFlow.

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<RaiseFault async="false" continueOnError="false" enabled="true" name="Raise-Fault-404">
    <DisplayName>Raise Fault-404</DisplayName>
    <Properties/>
    <FaultResponse>
        <Set>
            <Headers/>
            <Payload contentType="text/plain"/>
            <StatusCode>404</StatusCode>
            <ReasonPhrase>NotFound</ReasonPhrase>
        </Set>
    </FaultResponse>
    <IgnoreUnresolvedVariables>true</IgnoreUnresolvedVariables>
</RaiseFault>

So now I have a proxy with a base path of /v1/abc/def/* that can be accessed using

/v1/abc/def/003q000000RTElQ

/v1/Abc/dEf/003q000000RTElQ

/v1/ABC/DEF/003q000000RTElQ

/v1/aBc/DeF/003q000000RTElQ

and the above uris will point to proxy /v1/abc/def/003q000000RTElQ

If someone was to pass a url that does not match a proxy in our org we would return a 404

It works for us because it is a temporary solution until we can notify all are partners that we will be going to case sensitive uris and we don't lose any of the analytics .

Thanks. This helps.

Great Question & Great Answer, Thank you @jwerra & @crina.cimpian