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

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

问题描述

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

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

 

相关文章
|
7月前
|
API 网络安全 网络架构
【Azure APIM】解答REST API实现"禁用自签名证书的证书链验证"中的backends参数值从那里取值的问题?
本文介绍APIM服务调用后端API时因自签名证书导致500错误的解决方案。通过REST API禁用证书链验证,关键在于获取正确的backendId(即APIM中配置的Backend名称),并调用PATCH接口设置validateCertificateChain为false,从而解决SSL/TLS信任问题。
251 7
|
11月前
|
XML 安全 API
【Azure APIM】API Management的Policy是否支持 SAML assertion?
本文探讨了API Management是否支持通过策略(如validate-jwt)验证SAML assertion的问题。结论是API Management目前不支持SAML assertion验证,因其为XML-based token,而validate-jwt仅适用于JWT tokens。文章进一步介绍了SAML(安全断言标记语言)的基本原理、Assertion的组成及用途,包括单点登录(SSO)、跨组织身份验证、云服务集成和安全性增强等方面,帮助读者深入了解SAML的工作机制及其应用场景。
153 25
|
8月前
|
API 网络架构 容器
【Azure Container App】查看当前 Container App Environment 中的 CPU 使用情况的API
在扩展 Azure Container Apps 副本时,因 Container App Environment 的 CPU 核心数已达上限(500 cores),导致扩展失败。本文介绍如何使用 `az rest` 命令调用 Azure China Cloud 管理 API,查询当前环境的 CPU 使用情况,并提供具体操作步骤及示例。
287 17
|
7月前
|
XML 数据采集 API
用Lxml高效解析XML格式数据:以天气API为例
免费Python教程:实战解析中国天气网XML数据,详解Lxml库高效解析技巧、XPath用法、流式处理大文件及IP封禁应对策略,助你构建稳定数据采集系统。
382 0
|
8月前
|
安全 API 数据安全/隐私保护
【Azure 环境】Microsoft Graph API实现对Entra ID中应用生成密码的时间天数
本文介绍如何通过 Azure 的 App Management Policy 限制用户在创建 AAD 应用程序的 Client Secret 时设置最长 90 天的有效期。通过 Microsoft Graph API 配置 defaultAppManagementPolicy,可有效控制密码凭据的生命周期,增强安全管理。
202 4
|
JavaScript API C#
【Azure Developer】Python代码调用Graph API将外部用户添加到组,结果无效,也无错误信息
根据Graph API文档,在单个请求中将多个成员添加到组时,Python代码示例中的`members@odata.bind`被错误写为`members@odata_bind`,导致用户未成功添加。
207 10
|
API Python
【Azure Developer】分享一段Python代码调用Graph API创建用户的示例
分享一段Python代码调用Graph API创建用户的示例
210 11
|
API 开发工具 网络架构
【Azure Developer】如何通过Azure Portal快速获取到对应操作的API并转换为Python代码
【Azure Developer】如何通过Azure Portal快速获取到对应操作的API并转换为Python代码
156 1
|
7月前
|
缓存 监控 前端开发
顺企网 API 开发实战:搜索 / 详情接口从 0 到 1 落地(附 Elasticsearch 优化 + 错误速查)
企业API开发常陷参数、缓存、错误处理三大坑?本指南拆解顺企网双接口全流程,涵盖搜索优化、签名验证、限流应对,附可复用代码与错误速查表,助你2小时高效搞定开发,提升响应速度与稳定性。
|
7月前
|
JSON 算法 API
Python采集淘宝商品评论API接口及JSON数据返回全程指南
Python采集淘宝商品评论API接口及JSON数据返回全程指南