問題描述
使用APIM,在 Inbound 中對請求的Body內(nèi)容進(jìn)行解析辆琅。客戶端請求所傳遞的Request Body為XML格式,需要從Request Body中解析出多個(Element)節(jié)點(diǎn)值吨悍,然后設(shè)置通過(set-variable)為參數(shù)在后續(xù)使用。
但是驗(yàn)證發(fā)現(xiàn)蹋嵌,當(dāng)且只當(dāng)使用一個set-variable 從 Request Body中讀取數(shù)據(jù)時候育瓜,是可以成功的。如果要讀取第二個栽烂,第三個時躏仇,始終會遇見一個詭異的錯誤 Expression evaluation failed. Object reference not set to an instance of an object。 關(guān)鍵問題是愕鼓,為什么第一個可以成功钙态,第二個的語句和第一個完全一樣,卻面臨如此問題菇晃?真是詭異册倒!
需要解析的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的語句崇呵,兩者語法完全一樣。
- 但第二次就出現(xiàn)了 未將對象應(yīng)用到實(shí)例的異常馅袁。
錯誤截圖:
問題解決
經(jīng)過反復(fù)實(shí)驗(yàn)域慷,問題肯定出現(xiàn)在 context.Request.Body.As<XElement> 上,是不是這個內(nèi)容只能使用一次呢? 經(jīng) Google 搜尋犹褒,終于得出了官方解釋和解決辦法:
官方解釋
context.Request.Body.As<T> 和
context.Response.Body.As<T>方法用As<T>的方式指定讀取 Request 和 Response的Body內(nèi)容抵窒,默認(rèn)情況下,這個方式讀取的時原始消息的Body流叠骑,讀取一次后就變?yōu)椴豢捎美罨剩簿褪钦f只能 As<T>的方式一次。這就解釋了為什么第二個Set Variable語句出現(xiàn) Object 異常宙枷。
解決辦法
正如文檔中解釋掉房,使用 preserveContent : true 后,可以多次轉(zhuǎn)換 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文件動畫:
注意:
- 因?yàn)锳PIM實(shí)例的內(nèi)存存在限制,內(nèi)部的Memory限制為500MB璧帝,當(dāng)緩存的Request/Response的內(nèi)容大于500MB的時候捍岳,就會出現(xiàn) MessagePayLoadTooLarge異常。
- 當(dāng)使用 preserveContent:true 后睬隶,會把當(dāng)前的Body內(nèi)容緩存在APIM實(shí)例的內(nèi)存中锣夹,如果Body內(nèi)容大于500MB,則會出現(xiàn) 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
當(dāng)在復(fù)雜的環(huán)境中面臨問題恤左,格物之道需:濁而靜之徐清贴唇,安以動之徐生。 云中飞袋,恰是如此!
分類: 【Azure API 管理】
標(biāo)簽: APIM, APIM Policy 解析XML語句, preserveContent:true, Expression evaluation failed, set-variable