如何在 Azure API 管理策略中重用响应上下文变量?

Posted

技术标签:

【中文标题】如何在 Azure API 管理策略中重用响应上下文变量?【英文标题】:How Can I Reuse the Response Context Variable in Azure API Management Policy? 【发布时间】:2020-05-14 16:13:29 【问题描述】:

我正在使用 Azure API 管理解决方案编写概念证明。

我正在尝试编写一个 <inbound> 策略,它执行以下操作:

使用 <send-request> 向 API 的身份验证端点发出请求。 身份验证 API 返回两个密钥,它们必须作为 http 标头包含在内,以便能够向其他端点发出后续请求。 我正在使用 JObject 将来自身份验证 API 的响应解析为 json 然后我尝试从 json 对象的两个属性中读取值(它们是字符串) 然后,我将向不同的 API 端点发出后续请求,并设置两个 http 标头。标头的值必须是第一个(身份验证)响应中的两个变量。

这是我的政策目前的样子:

<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>

我遇到的问题是设置第二个标头(令牌 B)的值。看来我不能重用上下文变量(IResponse)context.Variables["auth"]

当我查看跟踪时,我看到第一个 &lt;set-header&gt; 策略的以下输出:

"message":"表达式评估成功。", “价值”:“xxxxxxxxxxxxxxxxxxxxx”

但是对于第二个&lt;set-header&gt; 政策,我得到:

"message":"表达式评估失败。", "details":"对象引用未设置为对象的实例。"

我可以看到,在第一个策略上调用方法后,我不再能够在第二个&lt;set-header&gt; 策略中以相同的方式重用上下文变量。

我试过了:

使用&lt;set-variable&gt; 设置令牌字符串。我相信&lt;set-variable&gt; 可以返回的类型数量有限,我无法返回IResponseJObject。如果我尝试转换为 json,然后提取每个属性(TokenA 和 TokenB)的字符串,我会遇到与上述相同的问题。

有人可以帮助我复制IResponse 对象或JObject 的语法,以便我可以使用.SelectToken() 读取它两次吗?我确定我误解了一个基本概念,但我远非一个经验丰富的 C# 开发人员!

谢谢

【问题讨论】:

【参考方案1】:

您收到错误的原因是您阅读响应正文的方式。当您将正文阅读为

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

您基本上是从上下文变量中处理正文。因此,下次您读取变量主体时,它会抛出臭名昭著的Object reference 错误。若要在 Azure API 管理中读取正文,需要在代码中使用 preservecontent 属性。所以你会读到如下所示的正文

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

执行此操作时,会保留原始正文,并且在评估中仅注入正文的副本。 您可以在以下位置找到有关此内容的详细文档 "Set Body- APIM policy"

【讨论】:

完美答案。谢谢曼达尔。不知道我是如何在文档中错过它的。 JObject 应该被支持作为变量的值。所以你应该能够做到 ()"/> 并稍后使用 tokens 变量.好处是您将解析一次 body。 如果不是因为这个,我可能永远找不到答案

以上是关于如何在 Azure API 管理策略中重用响应上下文变量?的主要内容,如果未能解决你的问题,请参考以下文章

如何使用 Azure API 管理设计微服务架构中的身份验证和授权策略?

从 Azure Apim 的上下文中读取命名值

如何在 Azure API 管理中使用 validate-jwt 策略验证使用 RS256 算法签名的 JWT

Azure API 管理策略 302

Azure API 管理不会在 4xx 上发送 CORS 标头

如何使用 Terraform 将 log-to-eventhub 策略添加到 Azure API 管理策略