Accessing backend service with Apigee JavaScript policy

We are trying to make RESTful POST request from JavaScript policy in an Apigee proxy to talk to our service endpoint and having trouble making it work. Our JavaScript code follows the pattern found in api-platform-samples/sample-proxies/outbound-oauth/apiproxy/resources/jsc/api-token-get.js. 

From TRACE tab, I found this line

print('responseObj: ' + JSON.stringify(responseObj));

outputs:

responseObj: undefined

We tested the endpoint itself with Postman and verified it works fine. Is the way httpClient is used in the code correct? 

 

 

try
{
    var key = context.getVariable(<variable name in string here>)
    var user = context.getVariable(<variable name in string here>)
    var password = context.getVariable(<vvariable name in string here>)

        url = <RESTful URL here>
        ...
        var bodyObj = {
            'Key': key,
            'User': user,
            'Password': password,
        };
        var req = new Request(url, 'POST', {}, serializeQuery(bodyObj));
        var exchange = httpClient.send(req);

        // Wait for the asynchronous POST request to finish
        exchange.waitForComplete(5000);

        if(exchange)
        {
            if (exchange.isSuccess()) 
            {
                var responseObj = exchange.getResponse().content.asJSON;
                print('responseObj: ' + JSON.stringify(responseObj));     
        ...
}

catch (err) {

    // Code to handle any error that may have happened previously by generating a response
    ...
}

function serializeQuery(obj) {
  var str = [];
  for (var p in obj) {
    str.push(encodeURIComponent(p) + "=" + encodeURIComponent(obj[p]));
  }
  return str.join("&");
}

 

 

 

Solved Solved
0 10 1,012
1 ACCEPTED SOLUTION

Thanks for the quick replies again!

Redirect issue: It turned out that the cause was WCF's behavior. We removed the trailing "/" from UriTemplate. Then redirect to a node happened without further update to the JavaScript code.
Timeout issuejavascript-executionTime was showing less than 200 ms (default value), but Postman call which invoked our Apigee proxy showed this error: "Execution of CustomerKeyToCustomerID2 failed with error: Javascript runtime exceeded limit of 200ms". So I bumped up timeLimit in JS policy to 2000 ms. And the timeout issue is resolved.
Reply deletion: I do not see Delete option. But I see Mute option. Muted replies are visible from me (of course. I may chose to unmute them later), but I wonder if they are hidden from you now. 

Anyway I will close this ticket. Thanks for your help, especially showing me a best-practice model of httpClient usage!

 

View solution in original post

10 REPLIES 10

No, not correct. 

First, there's not enough error handling. 

Second, it uses waitForComplete(), when it should use the callback mechanism. 

Try to follow this model: 

  function onComplete(response, error) {
    if (response) {
      context.setVariable('example.status', response.status);
    }
    else {
      context.setVariable('example.error', 'Whoops: ' + error);
    }
  }

  var payload = JSON.stringify({
     foo : 'bar',
     whatever : 1234
  });
  var headers = {
        'Content-Type' : 'application/json',
        'Authorization' : 'Bearer xyz'
      };
  var url = 'https://example.com/path2';
  var req = new Request(url, 'POST', headers, payload);

  httpClient.send(req, onComplete);

In your onComplete function, you can examine the response.content variable. You will also want to examine the response.headers collection to see the response data type. I never liked the `.asJSON` expression. It's more idiomatic to use JSON.parse() on the payload. Eg

var r = JSON.parse(response.content);
// r now contains an actual object

 

Thanks! Now I see the status 307, which is Temporary Redirect.

The URL is set to our load balancer. And when I hit each node, I get 504 (timed out) even though javascript-executionTime is 2 ms which is much smaller than javascript-timeLimit (200 ms) . 

When I use Postman, I have no problem with either way, load balancer URL or each node.

I wonder what is happening.

 

OK, I am not sure . Is there still a problem? 

Just a note: the httpClient in JS does not follow redirects automatically.  You need to do that "manually" if that's what you want. 

You made some remarks about  the 504 and a load balancer and  200ms and Postman etc.  I am not sure if you are identifying an obstacle, or an observation, or.. ?   Is there still a problem that needs solving? 

 

 

Thanks for the follow up!

There are two issues and you just helped me to clarify, redirect and timeout.

Regarding redirect, is there any samples I can get from Apigee site? Or could you please show me how. If not, what is the version of httpClient. It seems the way of redirecting is different depending on its version. 4.3, 4.2, older?

I tried these:

...
httpClient.setRedirectStrategy(new LaxRedirectStrategy()).build();
httpClient.send(req, onComplete);
...
...
httpClient.setRedirectStrategy(new LaxRedirectStrategy());
httpClient.send(req, onComplete);
...

In both cases, I get this:

TypeError: Cannot find default value for object.

If you kindly show me a model or point me to an example, it would be very helpful. Thanks!

 

Sorry. Please ignore my previous 2 replies.

 

I think you can delete your own replies, if you like. Click the "kebab" (three vertical dots) in the upper right of your reply.  That gives you "more options" and one of those options should be "delete". 

Regarding redirect, is there any samples I can get from Apigee site?

I don't know of samples in the Apigee documentation. It should be pretty easy for you to build if you are handy with JS. In the callback, just examine the status code. For 303, 307 etc, "follow" the Location header by invoking again. You would need the callback to retain the original payload if it is a 307.

 

var errorVariable = "js-error";

function httpSendFollowRedirects(url, verb, headers, payload, cb) {
  var redirectCount = 0;
  var onComplete = function(response, error) {
        if ( ! response) {
          context.setVariable(errorVariable, 'No response: ' + error);
          return;
        }
        if (response.status == 307) {
          redirectCount++;
          if (redirectCount < 10) {
            let newUrl = response.headers.Location;
            var req = new Request(newUrl, verb, headers, payload);
            httpClient.send(req, onComplete);
          }
          else {
            context.setVariable(errorVariable, 'Too many redirects');
            return;
          }
        }
        else {
          // handle non-302
          cb(response);
        }
      };
  var req = new Request(url, verb, headers, payload);
  httpClient.send(req, onComplete);
}

  var payload = JSON.stringify({
     foo : 'bar',
     whatever : 1234
  });
  var headers = {
        'Content-Type' : 'application/json',
        'Authorization' : 'Bearer xyz'
      };
var url = 'https://example.com/path2';

httpSendFollowRedirects(url, 'POST', headers, payload, function(response) {
  // handle successful response after redirects here.
  context.setVariable('js-status', response.status);
});

 

what is the version of httpClient. It seems the way of redirecting is different depending on its version. 4.3, 4.2, older?

I don't know what versioning you're referring to. I don't believe the httoClient available within JS callouts in Apigee proxies is versioned independently. You may be thinking of a different http client.

Thanks for the quick replies again!

Redirect issue: It turned out that the cause was WCF's behavior. We removed the trailing "/" from UriTemplate. Then redirect to a node happened without further update to the JavaScript code.
Timeout issuejavascript-executionTime was showing less than 200 ms (default value), but Postman call which invoked our Apigee proxy showed this error: "Execution of CustomerKeyToCustomerID2 failed with error: Javascript runtime exceeded limit of 200ms". So I bumped up timeLimit in JS policy to 2000 ms. And the timeout issue is resolved.
Reply deletion: I do not see Delete option. But I see Mute option. Muted replies are visible from me (of course. I may chose to unmute them later), but I wonder if they are hidden from you now. 

Anyway I will close this ticket. Thanks for your help, especially showing me a best-practice model of httpClient usage!

 

Nice !

 

I'm glad to hear you've resolved your questions, and things are working correctly for you now. 

And Thank you, for providing the details here, describing what worked for you. That will help current and future readers here!