简体   繁体   中英

How Can I Reuse the Response Context Variable in Azure API Management Policy?

I'm writing a proof of concept utilising the Azure API Management solution.

I am trying to write an <inbound> policy which does the following:

  • Uses <send-request> to make a request to an authentication endpoint of an API.
  • The authentication API returns two keys which must be included as http headers to be able to make subsequent requests to other endpoints.
  • I'm parsing the response from the authentication API as json using JObject
  • Then I'm trying to read the values from the two properties of the json object (they are strings)
  • Then, I will make a subsequent request to a different API endpoint, with two http headers set. The value of the headers must be the two variables from the first (authentication) response.

This is what my policy looks like currently:

<inbound>
  <base />

  <!-- Authenticate with the API and get authentication tokens for subsequent calls -->
  <send-request mode="new" response-variable-name="auth" timeout="20" ignore-error="true">
    <set-url>https://www.service.com/api/authenticate</set-url>
    <set-method>POST</set-method>
    <set-header name="Content-Type" exists-action="override">
      <value>application/json</value>
    </set-header>
    <set-body>
      @{
        var credentials = new JObject(); 

        credentials.Add(new JProperty("logonId", "{{API_LOGON_USERNAME}}")); 
        credentials.Add(new JProperty("logonPassword", "{{API_LOGON_PASSWORD}}")); 

        return credentials.ToString(); 
      }
    </set-body>
  </send-request>

  <!-- Make second query to a different endpoint, using the authentication tokens as http headers -->
  <send-request mode="new" response-variable-name="data" timeout="20" ignore-error="true">
    <set-url>https://www.service.com/api/data</set-url>
    <set-method>GET</set-method>
    <set-header name="TokenA" exists-action="override">
      <value>
        @{
          JObject identity = ((IResponse)context.Variables["auth"]).Body.As<JObject>(); 
          return identity.SelectToken("TokenA").ToString(); 
        }
      </value>
    </set-header>
    <set-header name="TokenB" exists-action="override">
      <value>
        @{ 
          JObject identity = ((IResponse)context.Variables["auth"]).Body.As<JObject>(); 
          return identity.SelectToken("TokenB").ToString(); 
        }
      </value>
    </set-header>
  </send-request>

  <!-- Return response from the second API -->
  <return-response response-variable-name="responseData">
    <set-status code="200" reason="OK" />
    <set-header name="Content-Type" exists-action="override">
      <value>application/json</value>
    </set-header>
    <set-body>
      @{ 
        JObject api_response = ((IResponse)context.Variables["data"]).Body.As<JObject>(); 
        return api_response.ToString();   
      }
    </set-body>
  </return-response>
</inbound>

The issue I am having is around setting the value for the second header (Token B). It appears that I cannot re-use the context variable (IResponse)context.Variables["auth"] .

When I review the trace, I see the following output for the first <set-header> policy:

"message":"Expression was successfully evaluated.", "value":"xxxxxxxxxxxxxxxxxxxxx"

But for the second <set-header> policy I get:

"message":"Expression evaluation failed.", "details":"Object reference not set to an instance of an object."

I can see that, after calling the method on the first policy, I am no longer able to reuse the context variable in the same way in the second <set-header> policy.

I have tried:

  • Setting the Token strings using <set-variable> . I believe there are a limited number of types that <set-variable> can return, and I cannot return the IResponse or the JObject . If I attempt to convert to json then extract the string of each property (TokenA and TokenB) I get the same issue as above.

Could someone please help me with the syntax to allow me to copy the IResponse object or the JObject so that I can read it twice using .SelectToken() ? I'm sure I'm misunderstanding a fundamental concept, but I am far from a seasoned C# developer!

Thanks

The reason you are getting the error is because of the way you are reading the response body. when you read the body as

JObject identity = ((IResponse)context.Variables["auth"]).Body.As<JObject>();

you are basically disposing of the body from the context variable. Hence the next time you read the variable body, it throws the infamous Object reference error. To work with reading the bodies in Azure API management, you need to use the preservecontent property in your code. SO you will read the body as shown below

JObject identity = ((IResponse)context.Variables["auth"]).Body.As<JObject>(preserveContent: true);

When you do this, the original body is preserved and only a copy of the body is injected in the evaluation. You can find in depth documentation about this at "Set Body- APIM policy"

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM