问题描述

使用APIM,在 Inbound 中对请求的Body内容进行解析。客户端请求所传递的Request Body为XML格式,需要从Request Body中解析出多个(Element)节点值,然后设置通过(set-variable)为参数在后续使用。

但是验证发现,当且只当使用一个set-variable 从 Request Body中读取数据时候,是可以成功的。如果要读取第二个,第三个时,始终会遇见一个诡异的错误 Expression evaluation failed. Object reference not set to an instance of an object。 关键问题是,为什么第一个可以成功,第二个的语句和第一个完全一样,却面临如此问题?真是诡异!

需要解析的XML格式如下:

<?xml version="1.0" encoding="utf-8"?>
<CDHotel xmlns="http://schemas.xmlsoap.org/soap/cdhotel/">
<Body>
<GetHotel xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://tempuri.org/">
<input>
<ID xmlns="http://schemas.datacontract.org/2014/01/wcf">202203081007001</ID>
<Name xmlns="http://schemas.datacontract.org/2014/01/wcf">Cheng Du Junyi Hotel</Name>
<Code xmlns="http://schemas.datacontract.org/2014/01/wcf">ICP1009100</Code>
</input>
</GetHotel>
</Body>
</CDHotel>

在APIM Policies中,需要获取 ID, Name, Code 和 Desc 值,策略语句如下:

<!--
IMPORTANT:
- Policy elements can appear only within the <inbound>, <outbound>, <backend> section elements.
- To apply a policy to the incoming request (before it is forwarded to the backend service), place a corresponding policy element within the <inbound> section element.
- To apply a policy to the outgoing response (before it is sent back to the caller), place a corresponding policy element within the <outbound> section element.
- To add a policy, place the cursor at the desired insertion point and select a policy from the sidebar.
- To remove a policy, delete the corresponding policy statement from the policy document.
- Position the <base> element within a section element to inherit all policies from the corresponding section element in the enclosing scope.
- Remove the <base> element to prevent inheriting policies from the corresponding section element in the enclosing scope.
- Policies are applied in the order of their appearance, from the top down.
- Comments within policy elements are not supported and may disappear. Place your comments between policy elements or at a higher level scope.
-->
<policies>
<inbound>
<base />
<set-variable name="myID" value="@(
context.Request.Body.As<XElement>().Descendants().FirstOrDefault(x => x.Name.LocalName == "ID")?.Value
)" />
<set-variable name="myName" value="@(
context.Request.Body.As<XElement>().Descendants().FirstOrDefault(x => x.Name.LocalName == "Name")?.Value
)" />
<set-variable name="myCode" value="@(
context.Request.Body.As<XElement>().Descendants().FirstOrDefault(x => x.Name.LocalName == "Code")?.Value
)" />
<set-variable name="myDesc" value="@(
context.Request.Body.As<XElement>().Descendants().FirstOrDefault(x => x.Name.LocalName == "Desc")?.Value
)" />
</inbound>
<backend>
<base />
</backend>
<outbound>
<set-header name="myID" exists-action="override">
<value>@((string)context.Variables["myID"])</value>
</set-header>
<set-header name="myName" exists-action="override">
<value>@((string)context.Variables["myName"])</value>
</set-header>
<set-header name="myCode" exists-action="override">
<value>@((string)context.Variables["myCode"])</value>
</set-header>
<set-header name="myDesc" exists-action="override">
<value>@((string)context.Variables["myDesc"])</value>
</set-header>
<base />
</outbound>
<on-error>
<base />
</on-error>
</policies>

在APIM的Test功能,查看Trace语句后,错误消息为:

set-variable (0.905 ms)
{
"message": "Expression was successfully evaluated.",
"expression": "\n context.Request.Body.As<XElement>().Descendants().FirstOrDefault(x => x.Name.LocalName == \"ID\")?.Value\n ",
"value": "202203081007001"
}
set-variable (0.013 ms)
{
"message": "Context variable was successfully set.",
"name": "myID",
"value": "202203081007001"
}
set-variable (7.898 ms)
{
"messages": [
{
"message": "Expression evaluation failed.",
"expression": "\n context.Request.Body.As<XElement>().Descendants().FirstOrDefault(x => x.Name.LocalName == \"Name\")?.Value\n ",
"details": "Object reference not set to an instance of an object."
},
"Expression evaluation failed. Object reference not set to an instance of an object.",
"Object reference not set to an instance of an object."
]
}

说明:

  • 绿色高亮部分为Set-Variable的语句,两者语法完全一样。
  • 但第二次就出现了 未将对象应用到实例的异常。

错误截图:

问题解决

经过反复实验,问题肯定出现在 context.Request.Body.As<XElement> 上,是不是这个内容只能使用一次呢? 经 Google 搜寻,终于得出了官方解释和解决办法:

官方解释

文档链接:https://docs.microsoft.com/en-us/azure/api-management/api-management-policy-expressions#ContextVariables

IMessageBody As<T>(preserveContent: bool = false): Where T: string, byte[],JObject, JToken, JArray, XNode, XElement, XDocument

The context.Request.Body.As<T> and context.Response.Body.As<T> methods are used to read either a request and response message body in specified type T. By default, the method:

  • Uses the original message body stream.
  • Renders it unavailable after it returns.

To avoid that and have the method operate on a copy of the body stream, set the preserveContent parameter to true, as in this example.

context.Request.Body.As<T> 和 context.Response.Body.As<T> 方法用As<T>的方式指定读取 Request 和 Response的Body内容,默认情况下,这个方式读取的时原始消息的Body流,读取一次后就变为不可用,也就是说只能 As<T>的方式一次。这就解释了为什么第二个Set Variable语句出现 Object 异常。

解决办法

正如文档中解释,使用 preserveContent : true 后,可以多次转换  Body Stream。

修改后的Policy为:

    <inbound>
<base />
<set-variable name="myID" value="@(
context.Request.Body.As<XElement>(preserveContent:true).Descendants().FirstOrDefault(x => x.Name.LocalName == "ID")?.Value
)" />
<set-variable name="myName" value="@(
context.Request.Body.As<XElement>(preserveContent:true).Descendants().FirstOrDefault(x => x.Name.LocalName == "Name")?.Value
)" />
<set-variable name="myCode" value="@(
context.Request.Body.As<XElement>(preserveContent:true).Descendants().FirstOrDefault(x => x.Name.LocalName == "Code")?.Value
)" />
<set-variable name="myDesc" value="@(
context.Request.Body.As<XElement>(preserveContent:true).Descendants().FirstOrDefault(x => x.Name.LocalName == "Desc")?.Value
)" />
</inbound>

修改后,测试解析XML文件动画:

注意:

  • 因为APIM实例的内存存在限制,内部的Memory限制为500MB,当缓存的Request/Response的内容大于500MB的时候,就会出现 MessagePayLoadTooLarge异常。
  • 当使用 preserveContent:true 后,会把当前的Body内容缓存在APIM实例的内存中,如果Body内容大于500MB,则会出现 MessagePayLoadTooLarge问题,所以对于Body Size过大的请求,不能使用 Buffer 及读取整个Response/Request Body在Policy代码中。

参考资料

API Management policy expressions - Context variable - IMessageBody : https://docs.microsoft.com/en-us/azure/api-management/api-management-policy-expressions#ContextVariables

Get an attribute value from XML Response in azure apim : https://stackoverflow.com/questions/68618339/get-an-attribute-value-from-xml-response-in-azure-apim

XElement Class : https://docs.microsoft.com/en-us/dotnet/api/system.xml.linq.xelement?view=net-6.0

【Azure API 管理】使用APIM进行XML内容读取时遇见的诡异错误 Expression evaluation failed. Object reference not set to an instance of an object.的更多相关文章

  1. Azure Sphere–“Object reference not set to an instance of an object” 解决办法

    在开发Azure Sphere应用时,如果出现项目无法编译,出现“Object reference not set to an instance of an object”时,必须从下面两个方面进行检 ...

  2. 【Azure API 管理】APIM集成内网虚拟网络后,启用自定义路由管理外出流量经过防火墙(Firewall),遇见APIs加载不出来问题

    问题描述 使用 Azure 虚拟网络,Azure APIM 可以管理无法通过 Internet 访问的 API,达到以保护企业内部的后端API的目的.在虚拟网络中,启用网络安全组(NSG:Networ ...

  3. 【Azure API 管理】APIM 配置Validate-JWT策略,验证RS256非对称(公钥/私钥)加密的Token

    问题描述 在APIM中配置对传入的Token进行预验证,确保传入后端被保护的API的Authorization信息正确有效,可以使用validate-jwt策略.validate-jwt 策略强制要求 ...

  4. 【Azure API 管理】APIM CORS策略设置后,跨域请求成功和失败的Header对比实验

    在文章"从微信小程序访问APIM出现200空响应的问题中发现CORS的属性[terminate-unmatched-request]功能"中分析了CORS返回空200的问题后,进一 ...

  5. 【Azure API 管理】在APIM 中添加 log-to-eventhub 策略,把 Request Body 信息全部记录在Event Hub中

    问题描述 根据文档 https://docs.azure.cn/zh-cn/api-management/api-management-howto-log-event-hubs, 可以将Azure A ...

  6. 【Azure API 管理】为调用APIM的请求启用Trace -- 调试APIM Policy的利器

    问题描述 在APIM中,通过门户上的 Test 功能,可以非常容易的查看请求的Trace信息,帮助调试 API 对各种Policy,在Inbound,Backend, Outbound部分的耗时问题, ...

  7. 【Azure API 管理】Azure APIM服务集成在内部虚拟网络后,在内部环境中打开APIM门户使用APIs中的TEST功能失败

    问题描述 使用微软API管理服务(Azure API Management),简称APIM. 因为公司策略要求只能内部网络访问,所以启用了VNET集成.集成方式见: (在内部模式下使用 Azure A ...

  8. 【Azure API 管理】解决API Management添加AAD Group时遇见的 Failed to query Azure Active Directory graph due to error 错误

    问题描述 为APIM添加AAD Group时候,等待很长很长的时间,结果添加失败.错误消息为: Write Groups ValidationError :Failed to query Azure ...

  9. 【Azure API 管理】API Management如何有效且快速更新呢?如对APIs/Policy等设置内容

    问题描述 APIM中的内容(API, Policy)等内容,如果有需要更新时候,通常可以在Azure APIM门户上操作,通过一个接口一个设置的修改,也可以针对一个接口导入/导出的方式修改.当APIM ...

随机推荐

  1. 洛谷P1002过河卒(60分)

    逻辑没问题,运行超时,得分60 写注释了,不多解释 import java.util.Scanner; public class D1 { static int a,b,c,d,con; //棋盘大小 ...

  2. NumPy 数组学习手册·翻译完成

    原文:Learning NumPy Array 协议:CC BY-NC-SA 4.0 欢迎任何人参与和完善:一个人可以走的很快,但是一群人却可以走的更远. 在线阅读 ApacheCN 面试求职交流群 ...

  3. [USACO19JAN]Exercise Route P

    先让我们探索一下两条非树边以及树边能构成简单环的条件是什么,你会发现将第一条非树边的两个点在树上形成的链记为 \(W_1\),另一条即为 \(W_2\),那么当且仅当 \(W_1, W_2\) 有交时 ...

  4. Junit4进行参数化测试

    @RunWith, 当类被@RunWith注解修饰,或者类继承了一个被该注解修饰的类,JUnit将会使用这个注解所指明的运行器(runner)来运行测试,而不是JUnit默认的运行器. 要进行参数化测 ...

  5. 有序取出Map集合的元素

    最近写到一个程序,返回了map,但是经过查阅资料,map是没有顺序的,各种查阅资料无果,最后自己写了这个方法.. 1,通过map集合的keySet()方法,获取到一个包含map所有key的Set集合 ...

  6. PHP宝典面试笔试题目

    PHP宝典面试笔试题目 来自<PHP程序员面试笔试宝典>,涵盖了近三年了各大型企业常考的PHP面试题,针对面试题提取出来各种面试知识也涵盖在了本书. PHP题目 [真题68] (    ) ...

  7. Solution -「APIO/CTSC 2007」「洛谷 P3620」数据备份

    \(\mathcal{Description}\)   Link.   给定升序序列 \(\{x_n\}\) 以及整数 \(k\),在 \(\{x_n\}\) 中选出恰 \(k\) 对 \((x_i, ...

  8. Git简单介绍以及使用入门

    Git Git:分布式版本控制系统, 此外还有 SVN (集中式版本控制系统) 下载地址(阿里云镜像) :CNPM Binaries Mirror (npmmirror.com) Git Bash : ...

  9. 简述对CT,IT,ICT,OT的认识

    今天碰到一个关键词:CT.CT领域,所以给自己做一个科普. 网络:简述对CT,IT,ICT,OT的认识 一.通信技术-CT(Communication Technology) 最早的CT业被称为电信业 ...

  10. 树莓派安装docker并使用docker图形化工具portainer

    配置环境 系统:Raspbian 11(64位) 设备:树莓派4B 一.安装docker 1.安装 curl -fsSL https://get.docker.com -o get-docker.sh ...