如何正确管理 WSO2 ESB 错误案例?我可以将所有错误情况集中在 faultSequence 中吗?
How can I correctly manage the WSO2 ESB error case? Can I centralize all the errors situation in the faultSequence?
我正在做一个WSO2 ESB项目,我对如何正确处理故障情况有一些疑问。
例如我有这个 API:
<?xml version="1.0" encoding="UTF-8"?>
<api context="/weather/crop_calendar" name="crop_calendar" xmlns="http://ws.apache.org/ns/synapse">
<resource methods="GET" uri-template="/country/{countryId}/crops?lang={lang_id}">
<inSequence>
<log level="full"/>
<property expression="get-property('uri.var.countryId')" name="countryId" scope="default" type="STRING"/>
<property expression="$url:lang" name="langId" scope="default" type="STRING"/>
<log level="custom">
<property expression="$ctx:countryId" name="countryId"/>
<property expression="$ctx:langId" name="langId"/>
</log>
<property name="messageType" scope="axis2" type="STRING" value="application/xml"/>
<payloadFactory media-type="xml">
<format>
<ds:GetCropCalendarCommoditiesList xmlns:ds="http://ws.wso2.org/dataservice">
<country_id></country_id>
<ds:language_id></ds:language_id>
</ds:GetCropCalendarCommoditiesList>
</format>
<args>
<arg evaluator="xml" expression="$ctx:countryId"/>
<arg evaluator="xml" expression="$ctx:langId"/>
</args>
</payloadFactory>
<header name="Action" scope="default" value="urn:SelectCropCalendarCommoditiesList"/>
<call>
<endpoint key="cropCalendarEndpoint"/>
</call>
<property name="messageType" scope="axis2" type="STRING" value="application/json"/>
<property expression="json-eval($.)" name="JSONPayload" scope="default" type="STRING"/>
<script language="js"><![CDATA[var log = mc.getServiceLog();
function scan(obj) {
var k;
if (obj instanceof Object) {
for (k in obj){
if (obj.hasOwnProperty(k)){
obj[k] = checkForNull(obj[k]);
//recursive call to scan property
scan(obj[k]);
}
}
} else {
//not an Object so obj[k] here is a value
}
}
function checkForNull(value) {
if (value instanceof Object && "@nil" in value) {
return null;
}
return value;
}
log.info("----------------------------- CROP CALENDARD COMMODITIS LISTING SCRIPT START ------------------------------------------------");
var lang_id = mc.getProperty('langId');
// stange workaround for getting JSON Payload. getPayloadJSON returned null.
var pl_string = mc.getProperty("JSONPayload");
log.info("PAYLOAD STRING: " + pl_string);
var payload = JSON.parse(pl_string);
// Create new response:
var response = payload;
log.info("RESPONSE: " + JSON.stringify(response));
scan(response);
// Fix the single element problem:
if (!(response.CropCalendarCommodities.CommoditiesList instanceof Array)) {
log.info("It is not an array, convert it into an array");
singleCommodity = response.CropCalendarCommodities.CommoditiesList;
response.CropCalendarCommodities.CommoditiesList = [];
response.CropCalendarCommodities.CommoditiesList.push(singleCommodity);
}
// Convert array of crops into required HATEOS format
var cropsList = new Array();
for (i = 0; i < response.CropCalendarCommodities.CommoditiesList.length; i++) {
log.info("Crop: " + i + " CROP NAME: " + response.CropCalendarCommodities.CommoditiesList[i].commodity_name);
commodityCropCalendarDetailsLinks = [];
commodityCropCalendarDetailsRefObj = {};
commodityCropCalendarDetailsRefObj.rel = "commodity_enutrifood_details";
commodityCropCalendarDetailsRefObj.href = "http://5.249.148.180:8280/weather/crop_calendar/commodity/" + checkForNull(response.CropCalendarCommodities.CommoditiesList[i].commodity_id) + "?lang=" + lang_id;
commodityCropCalendarDetailsRefObj.type = "GET";
commodityCropCalendarDetailsLinks.push(commodityCropCalendarDetailsRefObj);
response.CropCalendarCommodities.CommoditiesList[i].links = commodityCropCalendarDetailsLinks;
}
log.info("----------------------------- CROP CALENDARD COMMODITIS LISTING SCRIPT END ------------------------------------------------");
// put payload back
mc.setPayloadJSON(response);]]></script>
<property name="RESPONSE" scope="default" type="STRING" value="true"/>
<header action="remove" name="To" scope="default"/>
<send/>
</inSequence>
<outSequence/>
<faultSequence>
// HERE HANDLE THE ERROR CASES
</faultSequence>
</resource>
</api>
这个API在URL取2个输入参数:
- countryId
- lang_id
并使用两者执行 DSS 查询,然后查询输出在 JSON 文档中转换,然后 Script mediator 处理此 JSON 文档。
在此中介链中可能会发生错误。
例如传递一个 countryId 与一个国家相关,所以数据库中没有数据,DSS 查询 return 像这样(在 thge JSON转换):
{"CropCalendarCommodities":null}
问题是,当 Script 中介处理此文档时,WSO2 会出错,因为无法将此元素访问到前一个 JSON 中:
CropCalendarCommodities.CommoditiesList
我收到这样的错误:
TID: [-1234] [] [2018-06-13 10:26:58,527] ERROR {org.apache.synapse.mediators.bsf.ScriptMediator} - The script engine returned an error executing the inlined js script function mediate {org.apache.synapse.mediators.bsf.ScriptMediator}
com.sun.phobos.script.util.ExtendedScriptException: org.mozilla.javascript.EcmaError: TypeError: Cannot read property "CommoditiesList" from null (<Unknown Source>#45) in <Unknown Source> at line number 45
at com.sun.phobos.script.javascript.RhinoCompiledScript.eval(RhinoCompiledScript.java:68)
at javax.script.CompiledScript.eval(CompiledScript.java:92)
at org.apache.synapse.mediators.bsf.ScriptMediator.mediateForInlineScript(ScriptMediator.java:345)
at org.apache.synapse.mediators.bsf.ScriptMediator.invokeScript(ScriptMediator.java:265)
at org.apache.synapse.mediators.bsf.ScriptMediator.mediate(ScriptMediator.java:233)
at org.apache.synapse.mediators.AbstractListMediator.mediate(AbstractListMediator.java:97)
at org.apache.synapse.mediators.base.SequenceMediator.mediate(SequenceMediator.java:260)
at org.apache.synapse.core.axis2.Axis2SynapseEnvironment.mediateFromContinuationStateStack(Axis2SynapseEnvironment.java:775)
at org.apache.synapse.core.axis2.Axis2SynapseEnvironment.injectMessage(Axis2SynapseEnvironment.java:282)
at org.apache.synapse.core.axis2.SynapseCallbackReceiver.handleMessage(SynapseCallbackReceiver.java:554)
at org.apache.synapse.core.axis2.SynapseCallbackReceiver.receive(SynapseCallbackReceiver.java:188)
at org.apache.axis2.engine.AxisEngine.receive(AxisEngine.java:180)
at org.apache.synapse.transport.passthru.ClientWorker.run(ClientWorker.java:262)
at org.apache.axis2.transport.base.threads.NativeWorkerPool.run(NativeWorkerPool.java:172)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617)
at java.lang.Thread.run(Thread.java:745)
Caused by: org.mozilla.javascript.EcmaError: TypeError: Cannot read property "CommoditiesList" from null (<Unknown Source>#45)
我的疑问是:
处理这种情况的最佳方法是什么?
我知道我可以处理脚本调解器内部的情况(这很简单:我检查 CropCalendarCommodities.CommoditiesList 是否为 null 。如果它是空的,我创建一个 propper JSON 响应,将 returned 给用户(包含 404 状态消息的东西) .
但我的疑问是:我能否集中管理 ... 序列中所有可能的错误案例。
如果是,在这种情况下,我需要一种方法来检索我的 中发生的错误类型。
在这种情况下,如果错误出现在 脚本中介 中,我可以构建一个 404 状态消息,该消息将是 returned给用户。但是,如果发生另一个错误(例如 DSS 无法访问数据库),我将构建一个不同的错误消息,并将 returned 发送给用户。
我想做的是集中错误管理并以更类似于经典异常概念的方式处理错误情况。
我可以通过某种方式做到这一点吗?
是的,故障序列是处理错误的地方。你可以使用,
- get-属性('ERROR_CODE')
- get-属性('ERROR_MESSAGE')
您可以使用适当的 HTTP_SC 创建自定义错误负载并响应客户端。
发生错误时将调用故障序列,最好将其放在那里,否则客户端将一直等待响应。有关详细信息,请参阅 the error handling docs。
要记住的一件事是,在 DSS 中,如果数据库返回错误,这不会导致调用故障序列。您需要设置 FORCE_ERROR_ON_SOAP_FAULT 属性.
如果您有一组调解器,您希望以不同方式处理错误,请考虑将它们移到 Sequence(我们称之为 CodeSeq)。然后您创建另一个包含错误处理的序列(我们称之为 ErrorSeq)。然后在 CodeSeq 中你可以指定 on error 属性 onError="ErrorSeq"
.
我正在做一个WSO2 ESB项目,我对如何正确处理故障情况有一些疑问。
例如我有这个 API:
<?xml version="1.0" encoding="UTF-8"?>
<api context="/weather/crop_calendar" name="crop_calendar" xmlns="http://ws.apache.org/ns/synapse">
<resource methods="GET" uri-template="/country/{countryId}/crops?lang={lang_id}">
<inSequence>
<log level="full"/>
<property expression="get-property('uri.var.countryId')" name="countryId" scope="default" type="STRING"/>
<property expression="$url:lang" name="langId" scope="default" type="STRING"/>
<log level="custom">
<property expression="$ctx:countryId" name="countryId"/>
<property expression="$ctx:langId" name="langId"/>
</log>
<property name="messageType" scope="axis2" type="STRING" value="application/xml"/>
<payloadFactory media-type="xml">
<format>
<ds:GetCropCalendarCommoditiesList xmlns:ds="http://ws.wso2.org/dataservice">
<country_id></country_id>
<ds:language_id></ds:language_id>
</ds:GetCropCalendarCommoditiesList>
</format>
<args>
<arg evaluator="xml" expression="$ctx:countryId"/>
<arg evaluator="xml" expression="$ctx:langId"/>
</args>
</payloadFactory>
<header name="Action" scope="default" value="urn:SelectCropCalendarCommoditiesList"/>
<call>
<endpoint key="cropCalendarEndpoint"/>
</call>
<property name="messageType" scope="axis2" type="STRING" value="application/json"/>
<property expression="json-eval($.)" name="JSONPayload" scope="default" type="STRING"/>
<script language="js"><![CDATA[var log = mc.getServiceLog();
function scan(obj) {
var k;
if (obj instanceof Object) {
for (k in obj){
if (obj.hasOwnProperty(k)){
obj[k] = checkForNull(obj[k]);
//recursive call to scan property
scan(obj[k]);
}
}
} else {
//not an Object so obj[k] here is a value
}
}
function checkForNull(value) {
if (value instanceof Object && "@nil" in value) {
return null;
}
return value;
}
log.info("----------------------------- CROP CALENDARD COMMODITIS LISTING SCRIPT START ------------------------------------------------");
var lang_id = mc.getProperty('langId');
// stange workaround for getting JSON Payload. getPayloadJSON returned null.
var pl_string = mc.getProperty("JSONPayload");
log.info("PAYLOAD STRING: " + pl_string);
var payload = JSON.parse(pl_string);
// Create new response:
var response = payload;
log.info("RESPONSE: " + JSON.stringify(response));
scan(response);
// Fix the single element problem:
if (!(response.CropCalendarCommodities.CommoditiesList instanceof Array)) {
log.info("It is not an array, convert it into an array");
singleCommodity = response.CropCalendarCommodities.CommoditiesList;
response.CropCalendarCommodities.CommoditiesList = [];
response.CropCalendarCommodities.CommoditiesList.push(singleCommodity);
}
// Convert array of crops into required HATEOS format
var cropsList = new Array();
for (i = 0; i < response.CropCalendarCommodities.CommoditiesList.length; i++) {
log.info("Crop: " + i + " CROP NAME: " + response.CropCalendarCommodities.CommoditiesList[i].commodity_name);
commodityCropCalendarDetailsLinks = [];
commodityCropCalendarDetailsRefObj = {};
commodityCropCalendarDetailsRefObj.rel = "commodity_enutrifood_details";
commodityCropCalendarDetailsRefObj.href = "http://5.249.148.180:8280/weather/crop_calendar/commodity/" + checkForNull(response.CropCalendarCommodities.CommoditiesList[i].commodity_id) + "?lang=" + lang_id;
commodityCropCalendarDetailsRefObj.type = "GET";
commodityCropCalendarDetailsLinks.push(commodityCropCalendarDetailsRefObj);
response.CropCalendarCommodities.CommoditiesList[i].links = commodityCropCalendarDetailsLinks;
}
log.info("----------------------------- CROP CALENDARD COMMODITIS LISTING SCRIPT END ------------------------------------------------");
// put payload back
mc.setPayloadJSON(response);]]></script>
<property name="RESPONSE" scope="default" type="STRING" value="true"/>
<header action="remove" name="To" scope="default"/>
<send/>
</inSequence>
<outSequence/>
<faultSequence>
// HERE HANDLE THE ERROR CASES
</faultSequence>
</resource>
</api>
这个API在URL取2个输入参数:
- countryId
- lang_id
并使用两者执行 DSS 查询,然后查询输出在 JSON 文档中转换,然后 Script mediator 处理此 JSON 文档。
在此中介链中可能会发生错误。
例如传递一个 countryId 与一个国家相关,所以数据库中没有数据,DSS 查询 return 像这样(在 thge JSON转换):
{"CropCalendarCommodities":null}
问题是,当 Script 中介处理此文档时,WSO2 会出错,因为无法将此元素访问到前一个 JSON 中:
CropCalendarCommodities.CommoditiesList
我收到这样的错误:
TID: [-1234] [] [2018-06-13 10:26:58,527] ERROR {org.apache.synapse.mediators.bsf.ScriptMediator} - The script engine returned an error executing the inlined js script function mediate {org.apache.synapse.mediators.bsf.ScriptMediator}
com.sun.phobos.script.util.ExtendedScriptException: org.mozilla.javascript.EcmaError: TypeError: Cannot read property "CommoditiesList" from null (<Unknown Source>#45) in <Unknown Source> at line number 45
at com.sun.phobos.script.javascript.RhinoCompiledScript.eval(RhinoCompiledScript.java:68)
at javax.script.CompiledScript.eval(CompiledScript.java:92)
at org.apache.synapse.mediators.bsf.ScriptMediator.mediateForInlineScript(ScriptMediator.java:345)
at org.apache.synapse.mediators.bsf.ScriptMediator.invokeScript(ScriptMediator.java:265)
at org.apache.synapse.mediators.bsf.ScriptMediator.mediate(ScriptMediator.java:233)
at org.apache.synapse.mediators.AbstractListMediator.mediate(AbstractListMediator.java:97)
at org.apache.synapse.mediators.base.SequenceMediator.mediate(SequenceMediator.java:260)
at org.apache.synapse.core.axis2.Axis2SynapseEnvironment.mediateFromContinuationStateStack(Axis2SynapseEnvironment.java:775)
at org.apache.synapse.core.axis2.Axis2SynapseEnvironment.injectMessage(Axis2SynapseEnvironment.java:282)
at org.apache.synapse.core.axis2.SynapseCallbackReceiver.handleMessage(SynapseCallbackReceiver.java:554)
at org.apache.synapse.core.axis2.SynapseCallbackReceiver.receive(SynapseCallbackReceiver.java:188)
at org.apache.axis2.engine.AxisEngine.receive(AxisEngine.java:180)
at org.apache.synapse.transport.passthru.ClientWorker.run(ClientWorker.java:262)
at org.apache.axis2.transport.base.threads.NativeWorkerPool.run(NativeWorkerPool.java:172)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617)
at java.lang.Thread.run(Thread.java:745)
Caused by: org.mozilla.javascript.EcmaError: TypeError: Cannot read property "CommoditiesList" from null (<Unknown Source>#45)
我的疑问是:
处理这种情况的最佳方法是什么?
我知道我可以处理脚本调解器内部的情况(这很简单:我检查 CropCalendarCommodities.CommoditiesList 是否为 null 。如果它是空的,我创建一个 propper JSON 响应,将 returned 给用户(包含 404 状态消息的东西) .
但我的疑问是:我能否集中管理 ... 序列中所有可能的错误案例。
如果是,在这种情况下,我需要一种方法来检索我的 中发生的错误类型。
在这种情况下,如果错误出现在 脚本中介 中,我可以构建一个 404 状态消息,该消息将是 returned给用户。但是,如果发生另一个错误(例如 DSS 无法访问数据库),我将构建一个不同的错误消息,并将 returned 发送给用户。
我想做的是集中错误管理并以更类似于经典异常概念的方式处理错误情况。
我可以通过某种方式做到这一点吗?
是的,故障序列是处理错误的地方。你可以使用,
- get-属性('ERROR_CODE')
- get-属性('ERROR_MESSAGE')
您可以使用适当的 HTTP_SC 创建自定义错误负载并响应客户端。
发生错误时将调用故障序列,最好将其放在那里,否则客户端将一直等待响应。有关详细信息,请参阅 the error handling docs。
要记住的一件事是,在 DSS 中,如果数据库返回错误,这不会导致调用故障序列。您需要设置 FORCE_ERROR_ON_SOAP_FAULT 属性.
如果您有一组调解器,您希望以不同方式处理错误,请考虑将它们移到 Sequence(我们称之为 CodeSeq)。然后您创建另一个包含错误处理的序列(我们称之为 ErrorSeq)。然后在 CodeSeq 中你可以指定 on error 属性 onError="ErrorSeq"
.