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

本文涉及的产品
全局流量管理 GTM,标准版 1个月
公共DNS(含HTTPDNS解析),每月1000万次HTTP解析
云解析 DNS,旗舰版 1个月
简介: 【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

 

相关文章
|
3月前
|
SQL Java 数据库
flyway报错Caused by: org.flywaydb.core.api.FlywayException: Validate failed: Detected failed migration
flyway报错Caused by: org.flywaydb.core.api.FlywayException: Validate failed: Detected failed migration
58 1
|
3月前
|
测试技术 API
8-20|https://gitlab.xx.com/api/v4/projects/4/trigger/pipeline Request failed 状态码400
8-20|https://gitlab.xx.com/api/v4/projects/4/trigger/pipeline Request failed 状态码400
|
4月前
|
Docker 容器
成功解决:Caused by: ParsingException[Failed to parse object: expecting token of type [START_OBJECT] but
这篇文章讨论了在使用Docker启动Elasticsearch容器时遇到的一个具体问题:由于配置文件`elasticsearch.yml`解析出错导致容器启动失败。文章提供了详细的排查过程,包括查看容器的日志信息、检查并修正配置文件中的错误(特别是空格问题),并最终成功重新启动了容器。
|
4月前
|
API
【Azure API 管理】解决API Management添加AAD Group时遇见的 Failed to query Azure Active Directory graph due to error 错误
【Azure API 管理】解决API Management添加AAD Group时遇见的 Failed to query Azure Active Directory graph due to error 错误
|
4月前
|
API 数据安全/隐私保护 网络架构
【Azure API 管理】解决调用REST API操作APIM(API Management)需要认证问题(Authentication failed, The 'Authorization' header is missing)
【Azure API 管理】解决调用REST API操作APIM(API Management)需要认证问题(Authentication failed, The 'Authorization' header is missing)
|
5月前
|
JavaScript API 索引
JS【详解】Set 集合 (含 Set 集合和 Array 数组的区别,Set 的 API,Set 与 Array 的性能对比,Set 的应用场景)
JS【详解】Set 集合 (含 Set 集合和 Array 数组的区别,Set 的 API,Set 与 Array 的性能对比,Set 的应用场景)
83 0
|
6月前
|
SQL 分布式计算 DataWorks
DataWorks操作报错合集之调用ListInstances API时报错:ServiceUnavailable The request has failed due to a temporary failure of the server.是什么导致的
DataWorks是阿里云提供的一站式大数据开发与治理平台,支持数据集成、数据开发、数据服务、数据质量管理、数据安全管理等全流程数据处理。在使用DataWorks过程中,可能会遇到各种操作报错。以下是一些常见的报错情况及其可能的原因和解决方法。
|
7月前
|
算法 API Nacos
nacos 单节点Caused by: com.alibaba.nacos.api.exception.NacosException: failed to req API:/nacos/v1/ns/s
nacos 单节点Caused by: com.alibaba.nacos.api.exception.NacosException: failed to req API:/nacos/v1/ns/s
440 2
|
7月前
|
JavaScript
vue2中$set的原理_它对object属性做了啥?
vue2中$set的原理_它对object属性做了啥?
76 1
|
7月前
|
API 数据格式
8-20|https://gitlab.xx.com/api/v4/projects/4/trigger/pipeline Request failed状态码400
根据具体情况,逐步检查这些因素,找到引发400状态码的原因,并进行相应的修复。
102 0