问题描述

使用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格式如下:

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

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

  1. <!--
  2. IMPORTANT:
  3. - Policy elements can appear only within the <inbound>, <outbound>, <backend> section elements.
  4. - 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.
  5. - 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.
  6. - To add a policy, place the cursor at the desired insertion point and select a policy from the sidebar.
  7. - To remove a policy, delete the corresponding policy statement from the policy document.
  8. - Position the <base> element within a section element to inherit all policies from the corresponding section element in the enclosing scope.
  9. - Remove the <base> element to prevent inheriting policies from the corresponding section element in the enclosing scope.
  10. - Policies are applied in the order of their appearance, from the top down.
  11. - Comments within policy elements are not supported and may disappear. Place your comments between policy elements or at a higher level scope.
  12. -->
  13. <policies>
  14. <inbound>
  15. <base />
  16. <set-variable name="myID" value="@(
  17. context.Request.Body.As<XElement>().Descendants().FirstOrDefault(x => x.Name.LocalName == "ID")?.Value
  18. )" />
  19. <set-variable name="myName" value="@(
  20. context.Request.Body.As<XElement>().Descendants().FirstOrDefault(x => x.Name.LocalName == "Name")?.Value
  21. )" />
  22. <set-variable name="myCode" value="@(
  23. context.Request.Body.As<XElement>().Descendants().FirstOrDefault(x => x.Name.LocalName == "Code")?.Value
  24. )" />
  25. <set-variable name="myDesc" value="@(
  26. context.Request.Body.As<XElement>().Descendants().FirstOrDefault(x => x.Name.LocalName == "Desc")?.Value
  27. )" />
  28. </inbound>
  29. <backend>
  30. <base />
  31. </backend>
  32. <outbound>
  33. <set-header name="myID" exists-action="override">
  34. <value>@((string)context.Variables["myID"])</value>
  35. </set-header>
  36. <set-header name="myName" exists-action="override">
  37. <value>@((string)context.Variables["myName"])</value>
  38. </set-header>
  39. <set-header name="myCode" exists-action="override">
  40. <value>@((string)context.Variables["myCode"])</value>
  41. </set-header>
  42. <set-header name="myDesc" exists-action="override">
  43. <value>@((string)context.Variables["myDesc"])</value>
  44. </set-header>
  45. <base />
  46. </outbound>
  47. <on-error>
  48. <base />
  49. </on-error>
  50. </policies>

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

  1. set-variable (0.905 ms)
  2. {
  3. "message": "Expression was successfully evaluated.",
  4. "expression": "\n context.Request.Body.As<XElement>().Descendants().FirstOrDefault(x => x.Name.LocalName == \"ID\")?.Value\n ",
  5. "value": "202203081007001"
  6. }
  7. set-variable (0.013 ms)
  8. {
  9. "message": "Context variable was successfully set.",
  10. "name": "myID",
  11. "value": "202203081007001"
  12. }
  13. set-variable (7.898 ms)
  14. {
  15. "messages": [
  16. {
  17. "message": "Expression evaluation failed.",
  18. "expression": "\n context.Request.Body.As<XElement>().Descendants().FirstOrDefault(x => x.Name.LocalName == \"Name\")?.Value\n ",
  19. "details": "Object reference not set to an instance of an object."
  20. },
  21. "Expression evaluation failed. Object reference not set to an instance of an object.",
  22. "Object reference not set to an instance of an object."
  23. ]
  24. }

说明:

  • 绿色高亮部分为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为:

  1. <inbound>
  2. <base />
  3. <set-variable name="myID" value="@(
  4. context.Request.Body.As<XElement>(preserveContent:true).Descendants().FirstOrDefault(x => x.Name.LocalName == "ID")?.Value
  5. )" />
  6. <set-variable name="myName" value="@(
  7. context.Request.Body.As<XElement>(preserveContent:true).Descendants().FirstOrDefault(x => x.Name.LocalName == "Name")?.Value
  8. )" />
  9. <set-variable name="myCode" value="@(
  10. context.Request.Body.As<XElement>(preserveContent:true).Descendants().FirstOrDefault(x => x.Name.LocalName == "Code")?.Value
  11. )" />
  12. <set-variable name="myDesc" value="@(
  13. context.Request.Body.As<XElement>(preserveContent:true).Descendants().FirstOrDefault(x => x.Name.LocalName == "Desc")?.Value
  14. )" />
  15. </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. 近期Android学习

    近5天没有更新博客,因为这几天略微放下了python的学习,android这边连带项目比较急迫,先花大约1个星期的时间把重心放在Android,但python肯定还会坚持下去,毕竟连着学了那么久了. ...

  2. iBooker AI+财务提升星球 2020.4 热门讨论

    比特币量化套利的心路历程(附python量化招聘)(分享自知- 如何选择一份好的工作? 你知道为什么大家都想去好公司吗? 不- #财务知识# 可转债套利 辉丰转债128012套利之三个知道- #财务知 ...

  3. AT2651 [ARC077D] SS

    定义 \(nxt_i\) 表示在字符串 \(S\) 中以 \(i\) 结尾的最长 \(border\). 引理一:若 \(n - nxt_n \mid n\) 则 \(S_{1 \sim n - nx ...

  4. epoll反应堆模型实现

    epoll反应堆模型demo实现 在高并发TCP请求中,为了实现资源的节省,效率的提升,Epoll逐渐替代了之前的select和poll,它在用户层上规避了忙轮询这种效率不高的监听方式,epoll的时 ...

  5. Dubbo原理解析(非常透彻)

    一.概述 dubbo是一款经典的rpc框架,用来远程调用服务的. dubbo的作用: 面向接口的远程方法调用 智能容错和负载均衡 服务自动注册和发现. 自定义序列化协议 Dubbo 架构中的核心角色有 ...

  6. 面试官:谈谈你对IO流和NIO的理解

    一.概念 NIO即New IO,这个库是在JDK1.4中才引入的.NIO和IO有相同的作用和目的,但实现方式不同,NIO主要用到的是块,所以NIO的效率要比IO高很多.在Java API中提供了两套N ...

  7. Netty核心原理

    Netty核心原理 1. Netty介绍 1.1 原生NIO存在的问题 NIO的类库和API使用繁杂 需要具备其他额外的技能,如java多线程编程等才能编写出高质量的NIO程序 开发工作量和难度都非常 ...

  8. Solution -「UVA 1104」Chips Challenge

    \(\mathcal{Description}\)   Link.   在一个 \(n\times n\) 的方格图中,有一些格子已经放了零件,有一些格子可以放零件,其余格子不能放零件.求至多放多少个 ...

  9. CentOS7+Rsyslog+MySQL 搭建 Rsyslog 日志服务器

    文章目录 1.主机环境 2.rsyslog搭建 2.1.rsyslog-server搭建 2.2.rsyslog-client 2.2.1.测试 2.3.rsyslog日志分类 2.3.1.测试 3. ...

  10. wmware15安装centos7.9

    详细步骤如下: 下面位置应该写:D:\k8s\k8s-master01 也可以桥接 下面可以删除 从官方下载的,不需要test,所以选择第一个 默认英文的即可 改为上海 保持默认 配置静态ip 主机名 ...