How do I transform the path between the proxy and the target?

Not applicable

Our old backend uses PHP directly so exposes URLs like this...

GET /foo/bar.php?param=mumble

We want to isolate API consumers from that sort of implementation detail (as we will probably be changing the backend in future) so we would like to have...

GET /foo/bar?param=mumble<map to> GET /foo/bar.php?param=mumble on the backend.

The documentation explicitly states that putting the following in an AssignMessage policy should acheive the desired result...

<Set>
    <Path>/a/b/c</Path>
</Set>

But no matter where I put it in the flow, Apigee seems to use the public facing path to talk to the backend. I have seen various other examples involving hacking around with target.url and target.copy.pathsuffix but I haven't found anything which works for me.

Has anybody had to do this recently?

Is the a standard pattern for this

Solved Solved
5 32 33.2K
1 ACCEPTED SOLUTION

You can either use AssignMessage or a simple JS script policy to change the target URL. You need to have these policies under the TargetEndPoint flow, otherwise you don't see it work.

View solution in original post

32 REPLIES 32

You can either use AssignMessage or a simple JS script policy to change the target URL. You need to have these policies under the TargetEndPoint flow, otherwise you don't see it work.

Yes, and also, if you want to eliminate the proxy path from the target path, do not forget to set the context variable `target.copy.pathsuffix` to false. This also must be done in the target flow.

There's a detailed example in the docs on rewriting a target URL using a JavaScript policy.

Not applicable

Thanks all. It seems that the documentation in this case is not correct and the only way to do it is via target.url, in the target flow as @sudheendra1 suggested (which isn't very pretty)

<AssignMessage async="false" continueOnError="false" enabled="true" name="Transform-Request">
  <DisplayName>Transform Request</DisplayName>
   ...
  <AssignVariable>
    <Name>target.url</Name>
    <Value>{myhost}/foo/bar.php?{request.querystring}</Value>
  </AssignVariable>    
</AssignMessage>

It does not look like `target.copy.pathsuffix' is required in this case. It seems to only have an effect for me when I used Set/Path but not when I set the url directly.

The AssignVariable/Value element does not accept a Message Template. This means you cannot use a string like {myhost}/foo/bar.php?{request.querystring} and expect it to resolve those things within curly braces as variables.

Update: it is now possible to use a template in AssignVariable. Use <AssignVariable><Template>...</Template>

Instead what happens is: your variable gets the value {myhost}/foo/bar.php?{request.querystring} .

I just tested this with this policy:

<AssignMessage name="AM-1">
    <AssignVariable>
        <Name>foo.bar</Name>
      <!-- will not work>
        <Value>{system.timestamp}/foo/bar.php?{proxy.name}</Value>
    </AssignVariable>
    <AssignVariable>
        <Name>foo.do-templates-work</Name>
        <Value>We-shall-see</Value>
    </AssignVariable>
</AssignMessage>

And when I ran a request, I got this result:

5480-assignvariable-test-foo-bar.png

...which shows me that the Value element inside AssignVariable does not interpret curly braces as variable references.

There is a ticket asking for this behavior, APIRT-1180. The capability is not yet available in the product.


Update: Example of using a template in assignvariable:

<AssignMessage name="AM-1">
    <AssignVariable>
        <Name>foo.bar</Name>
        <Template>{system.timestamp}/foo/bar.php?{proxy.name}</Template>
    </AssignVariable>
</AssignMessage>

 

sgilson
Participant V

@sudheendra1 I am still missing something on changing the proxy path. I added an AssignMessage policy in the PreFlow of the TargetEndPoint:

<AssignMessage async="false" continueOnError="false" enabled="true" name="SetPath">
    <DisplayName>SetPath</DisplayName>
    <Set>
        <Path>/a/b/c</Path>
    </Set>
    <IgnoreUnresolvedVariables>true</IgnoreUnresolvedVariables>
    <AssignTo createNew="false" transport="http" type="request"/>
</AssignMessage>

But if you look at the trace, the request is still going to the default proxy endpoint:

960-screen-shot-2015-08-14-at-92350-am.png

I then set it by using the <AssignVariable> and it worked.

<AssignVariable>
    <Name>target.url</Name>
    <Value>http://weather.yahooapis.com/a/b/c?{request.querystring}</Value>
</AssignVariable> 

What is the <Set><Path> setting actually doing in the AssignMessage policy then?

Stephen

Thanks for the update. I look forward to hearing this has been resolved.

Not applicable

The behavior you are seeing is as designed.

The path that you set in the AssignMessage policy will get overwritten unless you also set target.copy.pathsuffix to false (as @Dino points out). In the target execution phase target.copy.pathsuffix is checked - if it is true (the default value) then the default path is used regardless of what you had previously done in the AssignMessage policy.

When you change the value of target.url via the AssignVariable policy - Apigee creates a variable (dynamic.target) which essentially acts the same as target.copy.pathsuffix being set to false. The target execution will not overwrite the path when dynamic.target exists.

While this may not be entirely consistent behavior, it is the way the system functions today.

Thanks @David Allen, but that is not working either. Here is my updated policy that sets target.copy.pathsuffix to false:

<AssignMessage async="false" continueOnError="false" enabled="true" name="SetPath">
    <DisplayName>SetPath</DisplayName>
    <Set>
        <Path>/a/b/c</Path>
    </Set>
    <AssignVariable>
        <Name>target.copy.pathsuffix</Name>
        <Value>false</Value>
    </AssignVariable>
    <IgnoreUnresolvedVariables>true</IgnoreUnresolvedVariables>
    <AssignTo createNew="false" transport="http" type="request"/>
</AssignMessage>

In the trace output, you can see that it is false but the Set/Path still does not take affect.

965-screen-shot-2015-08-14-at-12423-pm.png

It looks like there is an open issue in the latest release. This should be working as described above. I have filed the report.

Stephen

The bug mentioned @sgilson still occurs at this date. Will it ever be fixed, or will the documentation be updated to indicate that some other technique should be used?

@arghya das do you know when this fix could be expected?

@Birute Awasthi There are couple of bugs related to this feature. I have already bumped up the priority on those since it seems to be impacting multiple folks. However I don't have an ETA on the fixes yet.

Thanks @arghya das. Then in the meantime the best is to use a workaround described in the accepted answer on this thread.

@arghya das

I followed the documentation for Set Path in AssignMessage Policy which didnt work as expected.But Set Targeturl is working.

But I dont feel its good to hardcode the complete TargetUrl in the Assignmessage Policy rather giving URIPath alone.

Not sure whether the bug still exists.Kindly let me know if the issue has been fixed.

Guys! There has been a year and this issue is still open. Do something with it!

Any fixes for Set Path? target.url approach works for me.!

Not applicable

While the methods explained here work for a normal endpoint, nothing seems to get the job done when using ScriptTarget instead of HTTPTargetConnection.

@Miguel Balsevich for script target you can set the target.url variable

Late reply, but hopefully you can help. Where would you get the base URL from? target.url isn't set during the target preflow as far as I can see.

Any fixes for Set Path? target.url approach works for me.!

Not applicable

Hi,

I personally chose to go with Javascript policy.

if (context.flow=="TARGET_REQ_FLOW") {
    var path = context.getVariable("target.url").slice(0, -1) + context.getVariable("proxy.pathsuffix") + ".php?" + context.getVariable("request.querystring");
    context.setVariable("target.url", path);
    print("path", path);
}

Remember, Javascript has a performance penalty compared to policies. Doing this completely in policies would be best unless the path is too complex or dynamic.

Note that variable substitution may not work as expected in the AssignMessage policy. You will need to set the full URL as a variable first, and then reference the variable in the <Ref> section. Better to use Javascript policy

Not applicable

Any update for Set Path.Kindly let me know if the issue has been fixed.

adas
Participant V

The bug with Set Path has not been prioritized since there's a workaround in place. I just bumped up the priority for the bug: https://apigeesc.atlassian.net/browse/APIRT-1894

cc @Birute Awasthi @sgilson

@arghya das unfortunately there is no workaround available when you use a 'Target Server' configuration for HTTPTargetConnection. This is a blocker for such use cases.

This is a consolidated answer that takes the current findings into account. The current findings are that:

  • Using the <Set><Path> element along with the target.copy.pathsuffix set to false doesn't work. That's what the current open bug will address if and when it's fixed. We'll update the Assign Message docs to reflect this current outage.
  • Resetting the target.url variable IN THE TARGET ENDPOINT is ultimately the way to rewrite the target URL. You can do this either with the Assign Message policy or the JavaScript policy. JavaScript introduces a performance hit (as mentioned in this thread), but it lets you perform more elaborate, dynamic URL rewriting if that's what you need.

Rewriting the target.url in the TargetEndpoint is pretty simple and has already been covered in this thread. But what if you need to figure out back in the ProxyEndpoint, when the request first comes into the proxy, that the target.url needs to be rewritten with a certain value? You can't rewrite the target.url yet, because the flow hasn't reached the TargetEndpoint.

The attached API proxy shows you how.

path-transform.zip

To test it, add one of the following query parameters to the proxy URL:

?lebowski=true or ?lebowski=false

The functionality is similar in concept to doing conditional routing to different Target Endpoints. The attached proxy has comments in the XML, but here's the logic in a nutshell:

  1. The ProxyEndpoint has a conditional flow that checks the request for the value of the 'lebowski' query parameter.
  2. If the query parameter is 'true', a 'lebowski' variable gets populated with a hard-coded URL using an Assign Message policy.
  3. Now the TargetEndpoint has a conditional flow that checks whether or not the 'lebowski' variable has a value. If it does, it replaces the target.url with that value. If the 'lebowski' variable is null, the default TargetEndoint URL is used.

Here's a snapshot of the Assign Message policies:

ProxyEndpoint

Conditionally execute the following if (request.queryparam.lebowski = "true"). This sets the URL to use for overriding the target.url later, stored in a variable called 'lebowski'.)

<AssignMessage async="false" continueOnError="false" enabled="true" name="Assign-Message-1">
...
  <AssignVariable>
    <Name>lebowski</Name>
    <Value>http://mocktarget.apigee.net/user?user=Dude</Value>
  </AssignVariable>

TargetEndpoint (where target.url needs to be overridden)

Conditionally execute the following policy if (lebowski != null). That is, if the 'lebowski' variable has a URL in it, use it to override the target.url.

<AssignMessage async="false" continueOnError="false" enabled="true" name="Assign-Message-2">
...
  <AssignVariable>
    <Name>target.url</Name>
    <Ref>lebowski</Ref> <!-- References the variable created earlier. -->
  </AssignVariable>

Note that you could also handle the conditional logic (reading request.queryparam.lebowski) in the TargetEndpoint only. So this is just a more extreme example if you ever do need to set the URL in the ProxyEndpoint.

Hope this helps.

The @Floyd abides!

John
Participant I

Target URL is not enough. In my case, I need point to target connection

<HTTPTargetConnection>
  <LoadBalancer>
    <Server name="httpbin-test-ue1"/>
    <Server name="httpbin-test-uw2">
      <IsFallback>true</IsFallback>
    </Server>
  </LoadBalancer>
  <Path>/</Path>
</HTTPTargetConnection>


If I can set HTTPTargetConnection in javascript, it is great

If HTTPTargetConnection => LoadBalancer => Server can be variable. It will be great.
However, seems impossible.

 

Any update on this? It's been 8 years.

We're using a LoadBalancer and can't just hardcode the path variable. 

I can't extract the original target.url and adjust it because the variable is not defined during target preflow. No workaround works.

Ultimately ended up doing this to fix it:

Attach this JS policy to target endpoint pre-flow with a condition to only trigger on the path we want it on:

 

context.setVariable('target.copy.pathsuffix', false);
context.setVariable('dynamicSuffix', '/desired_suffix');

 

 Set up the load balancer like this:

 

    <HTTPTargetConnection>
        <LoadBalancer>
            <Server name="our_server"/>
        </LoadBalancer>
        <Path>/v1/{dynamicSuffix}</Path>
    </HTTPTargetConnection>

 

Finally apply this AssignMessage policy to target pre-flow one step before our JS policy, this time with no condition. Without this, the requests to paths we aren't transforming will fail as dynamicSuffix will be unresolved.

 

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<AssignMessage continueOnError="false" enabled="true" name="AM-SetEmptyDynamicSuffix">
    <DisplayName>AM-SetEmptyDynamicSuffix</DisplayName>
    <Properties/>
    <AssignVariable>
        <Name>dynamicSuffix</Name>
        <Value/>
    </AssignVariable>
    <AssignTo createNew="false" transport="http" type="request"/>
</AssignMessage>

 

 Way more complicated than it should be, but at least it works for my use case. Maybe someone will find this helpful.