(PHP 5, PHP 7, PHP
Введение
Представляет ошибку SOAP.
Обзор классов
public
?string
$_name = null;
protected
string
$file = «»;
private
array
$trace = [];
public __construct(
array|string|null $code
,
string $string
,
?string $actor
= null
,
mixed $details
= null
,
?string $name
= null
,
mixed $headerFault
= null
)
}
Свойства
- _name
- detail
- faultactor
- faultcode
- faultcodens
- faultstring
Содержание
- SoapFault::__construct — Конструктор SoapFault
- SoapFault::__toString — Получить текстовое представление SoapFault
dmitry dot koterov at gmail dot com ¶
13 years ago
You may use undocumented and invisible property $e->faultcode to access string version of $code. Because standard $e->getCode() does not work:
<?php
$e = new SoapFault("test", "msg");
var_dump($e->getCode()); // prints "0"
var_dump($e->faultcode); // prints "test"
?>
Also you may use namespaced fault codes:
<?php
$e = new SoapFault(array("namespace", "test"), "msg");
?>
- see ext/soap/soap.php, PHP_METHOD(SoapFault, SoapFault). To access the namespace, use $e->faultcodens
chris AT cmbuckley DOT co DOT uk ¶
12 years ago
A bit more digging in ext/soap/soap.c and the set_soap_fault function reveals the other undocumented properties from the constructor:
<?php
try {
throw new SoapFault('code', 'string', 'actor', 'detail', 'name', 'header');
} catch (Exception $ex) {
var_dump($ex->faultcode, $ex->faultstring, $ex->faultactor, $ex->detail, $ex->_name, $ex->headerfault);
}
?>
fbernoldi at gmail dot com ¶
10 years ago
Hi all,
I've decided to post this since it may be helpful, I've spend a couple of days trying to do this.
In order to use wsdl's specified faults with complex types, i.e:
WSDL definitions:
(xsd:schema namespace, ns1 = target namespace)
<xsd:element name="doubleFault">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="detail1" type="xsd:string"/>
<xsd:element name="detail2" type="xsd:string"/>
</xsd:sequence>
</xsd:complexType>
</xsd:element>
WSDL messages:
<message name="fault_specified">
<part name="relevant_name" element="ns1:doubleFault"/>
</message>
WSDL port type:
<portType name="test">
<operation name="operationTest">
<input message="ns1:not_relevant_request"/>
<output message="ns1:not_relevant_response"/>
<fault name="FaultSpecified" message="ns1:fault_specified"/>
....
</portType>
You have to specify the response in the detail parameter as an array corresponding the tag names.
PHP Code:
<?phpfunction operationTest($request_param ...) {// ...
$array_details = array("detail1" => "Explanation 1", "detail2" => "Explanation 2");
return new
SoapFault("Server", "example fault string", null, $array_details, "FaultSpecified");
}
$server = new SOAPServer("handmade.wsdl");
$server->addFunction("operationTest");
$server->handle(); ?>
that should respond something like this:
<SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/" xmlns:ns1="http://mynamespace">
<SOAP-ENV:Body>
<SOAP-ENV:Fault>
<faultcode>SOAP-ENV:Server</faultcode>
<faultstring>example fault string</faultstring>
<detail>
<ns1:doubleFault>
<detail1>Explanation 1</detail1>
<detail2>Explanation 2</detail2>
</ns1:doubleFault>
</detail>
</SOAP-ENV:Fault>
</SOAP-ENV:Body>
</SOAP-ENV:Envelope>
I Hope it helps,
Federico.
This XML file does not appear to have any style information associated with it. The document tree is shown below.
<wsdl:definitions xmlns:xsd=«http://www.w3.org/2001/XMLSchema» xmlns:wsdl=«http://schemas.xmlsoap.org/wsdl/» xmlns:tns=«http://soap.ws.javastudy.ru/» xmlns:soap=«http://schemas.xmlsoap.org/wsdl/soap/» xmlns:ns1=«http://schemas.xmlsoap.org/soap/http» name=«HelloSoap» targetNamespace=«http://soap.ws.javastudy.ru/»>
<wsdl:types>
<xs:schema xmlns:xs=«http://www.w3.org/2001/XMLSchema» xmlns:tns=«http://soap.ws.javastudy.ru/» attributeFormDefault=«unqualified» elementFormDefault=«unqualified» targetNamespace=«http://soap.ws.javastudy.ru/»>
<xs:element name=«exceptionTest» type=«tns:exceptionTest»/>
<xs:element name=«exceptionTestResponse» type=«tns:exceptionTestResponse»/>
<xs:element name=«getGoods» type=«tns:getGoods»/>
<xs:element name=«getGoodsResponse» type=«tns:getGoodsResponse»/>
<xs:element name=«goods» type=«tns:goods»/>
<xs:element name=«sayHelloTo» type=«tns:sayHelloTo»/>
<xs:element name=«sayHelloToResponse» type=«tns:sayHelloToResponse»/>
<xs:element name=«testService» type=«tns:testService»/>
<xs:element name=«testServiceResponse» type=«tns:testServiceResponse»/>
<xs:complexType name=«testService»>
<xs:sequence/>
</xs:complexType>
<xs:complexType name=«testServiceResponse»>
<xs:sequence>
<xs:element minOccurs=«0» name=«return» type=«xs:string»/>
</xs:sequence>
</xs:complexType>
<xs:complexType name=«getGoods»>
<xs:sequence/>
</xs:complexType>
<xs:complexType name=«getGoodsResponse»>
<xs:sequence>
<xs:element minOccurs=«0» name=«return» type=«tns:goods»/>
</xs:sequence>
</xs:complexType>
<xs:complexType name=«goods»>
<xs:sequence>
<xs:element name=«id» type=«xs:int»/>
<xs:element minOccurs=«0» name=«name» type=«xs:string»/>
</xs:sequence>
</xs:complexType>
<xs:complexType name=«sayHelloTo»>
<xs:sequence>
<xs:element minOccurs=«0» name=«text» type=«xs:string»/>
</xs:sequence>
</xs:complexType>
<xs:complexType name=«sayHelloToResponse»>
<xs:sequence>
<xs:element minOccurs=«0» name=«return» type=«xs:string»/>
</xs:sequence>
</xs:complexType>
<xs:complexType name=«exceptionTest»>
<xs:sequence>
<xs:element minOccurs=«0» name=«text» type=«xs:string»/>
</xs:sequence>
</xs:complexType>
<xs:complexType name=«exceptionTestResponse»>
<xs:sequence/>
</xs:complexType>
<xs:complexType name=«exceptionTrace»>
<xs:sequence>
<xs:element minOccurs=«0» name=«trace» type=«xs:string»/>
</xs:sequence>
</xs:complexType>
<xs:element name=«MyWebserviceException» type=«tns:MyWebserviceException»/>
<xs:complexType name=«MyWebserviceException»>
<xs:sequence>
<xs:element minOccurs=«0» name=«exceptionTrace» type=«tns:exceptionTrace»/>
<xs:element minOccurs=«0» name=«message» type=«xs:string»/>
</xs:sequence>
</xs:complexType>
</xs:schema>
</wsdl:types>
<wsdl:message name=«testService»>
<wsdl:part element=«tns:testService» name=«parameters»></wsdl:part>
</wsdl:message>
<wsdl:message name=«exceptionTestResponse»>
<wsdl:part element=«tns:exceptionTestResponse» name=«parameters»></wsdl:part>
</wsdl:message>
<wsdl:message name=«getGoods»>
<wsdl:part element=«tns:getGoods» name=«parameters»></wsdl:part>
</wsdl:message>
<wsdl:message name=«testServiceResponse»>
<wsdl:part element=«tns:testServiceResponse» name=«parameters»></wsdl:part>
</wsdl:message>
<wsdl:message name=«sayHelloTo»>
<wsdl:part element=«tns:sayHelloTo» name=«parameters»></wsdl:part>
</wsdl:message>
<wsdl:message name=«exceptionTest»>
<wsdl:part element=«tns:exceptionTest» name=«parameters»></wsdl:part>
</wsdl:message>
<wsdl:message name=«getGoodsResponse»>
<wsdl:part element=«tns:getGoodsResponse» name=«parameters»></wsdl:part>
</wsdl:message>
<wsdl:message name=«MyWebserviceException»>
<wsdl:part element=«tns:MyWebserviceException» name=«MyWebserviceException»></wsdl:part>
</wsdl:message>
<wsdl:message name=«sayHelloToResponse»>
<wsdl:part element=«tns:sayHelloToResponse» name=«parameters»></wsdl:part>
</wsdl:message>
<wsdl:portType name=«WebserviceSEI»>
<wsdl:operation name=«testService»>
<wsdl:input message=«tns:testService» name=«testService»></wsdl:input>
<wsdl:output message=«tns:testServiceResponse» name=«testServiceResponse»></wsdl:output>
</wsdl:operation>
<wsdl:operation name=«getGoods»>
<wsdl:input message=«tns:getGoods» name=«getGoods»></wsdl:input>
<wsdl:output message=«tns:getGoodsResponse» name=«getGoodsResponse»></wsdl:output>
</wsdl:operation>
<wsdl:operation name=«sayHelloTo»>
<wsdl:input message=«tns:sayHelloTo» name=«sayHelloTo»></wsdl:input>
<wsdl:output message=«tns:sayHelloToResponse» name=«sayHelloToResponse»></wsdl:output>
</wsdl:operation>
<wsdl:operation name=«exceptionTest»>
<wsdl:input message=«tns:exceptionTest» name=«exceptionTest»></wsdl:input>
<wsdl:output message=«tns:exceptionTestResponse» name=«exceptionTestResponse»></wsdl:output>
<wsdl:fault message=«tns:MyWebserviceException» name=«MyWebserviceException»></wsdl:fault>
</wsdl:operation>
</wsdl:portType>
<wsdl:binding name=«HelloSoapSoapBinding» type=«tns:WebserviceSEI»>
<soap:binding style=«document» transport=«http://schemas.xmlsoap.org/soap/http»/>
<wsdl:operation name=«testService»>
<soap:operation soapAction=«» style=«document»/>
<wsdl:input name=«testService»>
<soap:body use=«literal»/>
</wsdl:input>
<wsdl:output name=«testServiceResponse»>
<soap:body use=«literal»/>
</wsdl:output>
</wsdl:operation>
<wsdl:operation name=«getGoods»>
<soap:operation soapAction=«» style=«document»/>
<wsdl:input name=«getGoods»>
<soap:body use=«literal»/>
</wsdl:input>
<wsdl:output name=«getGoodsResponse»>
<soap:body use=«literal»/>
</wsdl:output>
</wsdl:operation>
<wsdl:operation name=«sayHelloTo»>
<soap:operation soapAction=«» style=«document»/>
<wsdl:input name=«sayHelloTo»>
<soap:body use=«literal»/>
</wsdl:input>
<wsdl:output name=«sayHelloToResponse»>
<soap:body use=«literal»/>
</wsdl:output>
</wsdl:operation>
<wsdl:operation name=«exceptionTest»>
<soap:operation soapAction=«» style=«document»/>
<wsdl:input name=«exceptionTest»>
<soap:body use=«literal»/>
</wsdl:input>
<wsdl:output name=«exceptionTestResponse»>
<soap:body use=«literal»/>
</wsdl:output>
<wsdl:fault name=«MyWebserviceException»>
<soap:fault name=«MyWebserviceException» use=«literal»/>
</wsdl:fault>
</wsdl:operation>
</wsdl:binding>
<wsdl:service name=«HelloSoap»>
<wsdl:port binding=«tns:HelloSoapSoapBinding» name=«HelloSoapPort»>
<soap:address location=«http://localhost:8080/soap/webserviceSEI»/>
</wsdl:port>
</wsdl:service>
</wsdl:definitions>
If an error occurs during processing, the response to a SOAP message is a SOAP fault element in the body of the message, and the fault is returned to the sender of the SOAP message.
The SOAP fault mechanism returns specific information about the error, including a predefined code, a description, and the address of the SOAP processor that generated the fault.
Points to Note
-
A SOAP message can carry only one fault block.
-
Fault is an optional part of a SOAP message.
-
For HTTP binding, a successful response is linked to the 200 to 299 range of status codes.
-
SOAP Fault is linked to the 500 to 599 range of status codes.
Sub-elements of Fault
The SOAP Fault has the following sub elements −
Sr.No | Sub-element & Description |
---|---|
1 |
<faultCode> It is a text code used to indicate a class of errors. See the next Table for a listing of predefined fault codes. |
2 |
<faultString> It is a text message explaining the error. |
3 |
<faultActor> It is a text string indicating who caused the fault. It is useful if the SOAP message travels through several nodes in the SOAP message path, and the client needs to know which node caused the error. A node that does not act as the ultimate destination must include a faultActor element. |
4 |
<detail> It is an element used to carry application-specific error messages. The detail element can contain child elements called detail entries. |
SOAP Fault Codes
The faultCode values defined below must be used in the faultcode element while describing faults.
Sr.No | Error & Description |
---|---|
1 |
SOAP-ENV:VersionMismatch Found an invalid namespace for the SOAP Envelope element. |
2 |
SOAP-ENV:MustUnderstand An immediate child element of the Header element, with the mustUnderstand attribute set to «1», was not understood. |
3 |
SOAP-ENV:Client The message was incorrectly formed or contained incorrect information. |
4 |
SOAP-ENV:Server There was a problem with the server, so the message could not proceed. |
SOAP Fault Example
The following code is a sample Fault. The client has requested a method named ValidateCreditCard, but the service does not support such a method. This represents a client request error, and the server returns the following SOAP response −
<?xml version = '1.0' encoding = 'UTF-8'?> <SOAP-ENV:Envelope xmlns:SOAP-ENV = "http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsi = "http://www.w3.org/1999/XMLSchema-instance" xmlns:xsd = "http://www.w3.org/1999/XMLSchema"> <SOAP-ENV:Body> <SOAP-ENV:Fault> <faultcode xsi:type = "xsd:string">SOAP-ENV:Client</faultcode> <faultstring xsi:type = "xsd:string"> Failed to locate method (ValidateCreditCard) in class (examplesCreditCard) at /usr/local/ActivePerl-5.6/lib/site_perl/5.6.0/SOAP/Lite.pm line 1555. </faultstring> </SOAP-ENV:Fault> </SOAP-ENV:Body> </SOAP-ENV:Envelope>
Error Handling with SOAP Faults
SOAP errors are handled using a specialized envelope known as a
Fault Envelope. If an error occurs
while the server processes a SOAP message, it constructs a SOAP Fault
and sends it back to the client. Here’s a typical
SOAP 1.1 Fault:
<?xml version='1.0' encoding='UTF-8'?> <SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/" xmlns: xsi="http://www.w3.org/1999/XMLSchema-instance" xmlns:xsd="http://www.w3.org/1999/ XMLSchema"> <SOAP-ENV:Body> <SOAP-ENV:Fault> <faultcode>SOAP-ENV:Server</faultcode> <faultstring>Test Fault</faultstring> <faultactor>/soap/servlet/rpcrouter</faultactor> <detail> <stackTrace>[SOAPException: faultCode=SOAP-ENV:Server; msg=Test Fault] at StockQuantity.getQty(StockQuantity.java:21) at java.lang.reflect.Method.invoke(Native Method) at org.apache.soap.server.RPCRouter.invoke(RPCRouter.java:146) ... at org.apache.tomcat.util.ThreadPool$ControlRunnable.run( ThreadPool.java:501) at java.lang.Thread.run(Thread.java:498) </stackTrace> </detail> </SOAP-ENV:Fault> </SOAP-ENV:Body> </SOAP-ENV:Envelope>
A SOAP Fault is a special element that must appear as an immediate
child of the SOAP body element. The
<faultcode>
and
<faultstring>
elements are required. The
<faultactor>
and
<detail>
elements are optional. Table 4-1 lists the possible values for the faultcodes
and their meanings.
Table 4-1. SOAP faultcodes
Faultcode |
Meaning |
---|---|
|
The SOAP node processing the request encountered a version mismatch. |
|
An immediate child element of the SOAP header (i.e., |
|
Introduced in SOAP 1.2 Working Draft 12/17/2001. It is an error for a |
|
The |
|
The content generated by the client is incorrect or malformed. |
|
The content sent by the client is perfectly acceptable, but the SOAP |
The body and Fault elements are namespace-qualified to the
envelope’s namespace—for example,
<SOAP-ENV:body>
and
<SOAP-ENV:Fault>
. The
<faultcode>
element uses the local namespace
(it has no namespace prefix), and the
<faultcode>
value that the element contains
is a qualified name using the envelope’s
namespace—for example,
<faultcode>SOAP-ENV:Client</faultcode>
.
The SOAP Fault from the previous listing was achieved by making a
slight modification to the StockQuantity
service.
In Apache SOAP, having the service throw
an exception is all that’s needed to generate a
fault; Apache takes care of the rest:
public class StockQuantity{ public int getQty (String item) throws org.apache.soap.SOAPException { int inStockQty = (int)(java.lang.Math.random( ) * (double)1000); if (item.equalsIgnoreCase("Fail")) throw new org.apache.soap.SOAPException (org.apache.soap.Constants.FAULT_CODE_SERVER, "Test Fault"); return inStockQty; } ... }
In Apache SOAP 2.2, this code is all that is necessary to send a
complete SOAP 1.1 Fault message back to the sender. To view the full
output of the Fault message, redirect the
CheckStock
RPC call through the TunnelGui utility
by using the command:
java CheckStock -url http://localhost:5555/soap/servlet/rpcrouter -item Fail
In this command, 5555
is the port on which the
TunnelGui is listening. The RPC request and the corresponding SOAP
Fault can be viewed in the TunnelGui window, as shown in Figure 4-1.
Figure 4-1. A SOAP Fault viewed through the Apache TunnelGui utility
The sending client can trap the Fault programatically and take
appropriate action. Apache
SOAP has a Fault
object that can be used to access
the pieces of the Fault message, as indicated in this excerpt from
CheckStock
:
//Invoke the service Response resp = call.invoke (url,"urn:go-do-this"); //Check the response if (resp != null) { if (resp.generatedFault ( )) { Fault fault = resp.getFault ( ); System.out.println ("Call failed due to a SOAP Fault: "); System.out.println (" Fault Code = " + fault.getFaultCode ( )); System.out.println (" Fault String = " + fault.getFaultString ( ));
While the ability to generate a fault by throwing an exception is
handy, you may want more control over what goes into a fault message.
For example, Apache SOAP, by default, puts the current stacktrace
into the <detail>
element of the SOAP fault.
That may not be what you want. We will explore how to build your own
Fault message in the context of the mustUnderstand
attribute.
Soap Faults and the mustUnderstand Attribute
To appreciate the meaning and role of the
mustUnderstand
or misUnderstood
fault codes, one must first understand the intent of the
mustUnderstand
attribute. This attribute can be
placed in any top-level header element. The presence of the
mustUnderstand
attribute with a value of
true
or 1
means that the header
element must be recognizable by the receiving SOAP processor. If the
SOAP processor does not recognize or know how to process the header
element, it must generate a Fault. We can generate a header element
with a mustUnderstand
attribute by adding the
following line of code to our
GenericHTTPSoapClient
:
// Create a header element in a namespace
org.w3c.dom.Element headerElement =
doc.createElementNS(URI,"jaws:MessageHeader");
headerElement.setAttributeNS(URI,"SOAP-ENV:mustUnderstand","1");
// Create subnodes within the MessageHeader
org.w3c.dom.Element ele = doc.createElement("From");
org.w3c.dom.Text textNode = doc.createTextNode("Me");
This code creates a SOAP envelope that looks like this:
<SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsi="http://www.w3.org/1999/XMLSchema-instance" xmlns:xsd="http://www.w3.org/1999/XMLSchema"> <SOAP-ENV:Header> <jaws:MessageHeader xmlns:jaws="urn:http://oreilly/jaws/samples" SOAP-ENV:MustUnderstand="1" > <From>Me</From> <To>You</To> <MessageId>9999</MessageId> ... </jaws:MessageHeader> </SOAP-ENV:Header> <SOAP-ENV:Body> ... </SOAP-ENV:Body> </SOAP-ENV:Envelope>
This envelope requires the server to understand the
<MessageHeader>
element. Since the server
doesn’t understand these elements, it returns a SOAP
1.1 Fault message:
<?xml version='1.0' encoding='UTF-8'?>
<SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/" xmlns:
xsi="http://www.w3.org/1999/XMLSchema-instance" xmlns:xsd="http://www.w3.org/1999/
XMLSchema">
<SOAP-ENV:Body>
<SOAP-ENV:Fault>
<faultcode>SOAP-ENV:MustUnderstand</faultcode>
<faultstring>Unsupported header: jaws:MessageHeader</faultstring>
<faultactor>/examples/servlet/FaultServlet</faultactor>
</SOAP-ENV:Fault>
</SOAP-ENV:Body>
</SOAP-ENV:Envelope>
The code used to generate this fault is in the following listing of
the FaultServlet
class.
FaultServlet
is a variation of our
HTTPReceive
class. As part of the
header’s processing, we look for the existence of a
mustUnderstand
attribute:
public class FaultServlet extends HttpServlet
{
...
public void doPost(HttpServletRequest request, HttpServletResponse response)
throws IOException, ServletException
{
...
// Get the header and check it for mustunderstand
Header header = env.getHeader( );
java.util.Vector headerEntries = header.getHeaderEntries( );
screenWriter.write("nHeader==>n");
for (java.util.Enumeration e = headerEntries.elements( );
e.hasMoreElements( );)
{
org.w3c.dom.Element el = (org.w3c.dom.Element)e.nextElement( );
org.apache.soap.util.xml.DOM2Writer.serializeAsXML(
(org.w3c.dom.Node)el, screenWriter);
// process mustUnderstand
String mustUnderstand=
el.getAttributeNS(Constants.NS_URI_SOAP_ENV,
"mustUnderstand");
screenWriter.write("nMustUnderstand: "
+ mustUnderstand + "n");
String tagName = el.getTagName( );
screenWriter.write("Tag Name: " + tagName + "n");
FaultServlet
doesn’t support the
<MessageHeader>
tag; it supports only the
<IOnlyUnderstandThis>
tag. Therefore, we
must generate a fault when it sees the message header tag combined
with the mustUnderstand
attribute. To construct
the fault, we create a SOAPException
and use it to
create a new Fault
object:
if(!tagName.equalsIgnoreCase("IOnlyUnderstandThis")) { //generate a fault. screenWriter.write("Unsupported header: " + tagName + "n"); screenWriter.write("Generating Fault....n"); SOAPException se = new SOAPException(Constants.FAULT_CODE_MUST_UNDERSTAND, "Unsupported header: " + tagName); Fault fault = new Fault(se); fault.setFaultActorURI (request.getRequestURI ( )); String respEncStyle = Constants.NS_URI_SOAP_ENC;
Next, we create a Response
object and supply it
with the Fault
object that we created:
org.apache.soap.rpc.Response soapResponse =
new org.apache.soap.rpc.Response (
null, // targetObjectURI
null, // methodName
fault,
null, // params
null, // headers
respEncStyle, // encodingStyleURI
null); // SOAPContext
Finally, we create an Envelope
from the
Response
object and marshall it into the
PrintWriter
attached to the
servlet’s HTTPResponse
:
Envelope faultEnvelope = soapResponse.buildEnvelope( ); org.apache.soap.encoding.SOAPMappingRegistry smr = new org.apache.soap.encoding.SOAPMappingRegistry( ); PrintWriter resW = response.getWriter( ); faultEnvelope.marshall(resW, smr, soapResponse.getSOAPContext( )); response.setContentType(request.getContentType( )); response.setStatus(response.SC_INTERNAL_SERVER_ERROR); ... } }
Note that in the SOAP 1.2 effort, there has been much debate over
whether mustUnderstand
also means
“MustExecute” or
“MustProcess.”
SOAP 1.2 clarifies the use of the SOAP header in Fault processing.
The general idea is that the body of a Fault message should contain
only the errors that resulted from processing the body of the message
that caused the Fault. Likewise, detailed information about any
errors that occur as the result of processing a header block should
be placed in the header block of the resulting Fault message. The
<Fault>
and
<Faultcode>
elements still appear in the
body. However, the <Misunderstood>
element
in the header carries detailed information about which header element
could not be recognized.
The SOAP 1.2 Fault message (generated from not being able to
understand the <MessageHeader>
element in
our previous example) would look like this:
<env:Envelope xmlns:env='http://www.w3.org/2001/09/soap-envelope' xmlns:f='http://www.w3.org/2001/09/soap-faults' > <env:Header> <f:misunderstood qname="jaws:MessageHeader" xmlns:jaws="urn:http://oreilly/jaws/samples" /> </env:Header> <env:Body> <env:Fault> <faultcode>env:mustUnderstand</faultcode> <faultstring> One or more mandatory headers not understood </faultstring> </env:Fault> </env:Body> </env:Envelope>
SOAP 1.2 adds an additional set of fault codes. These RPC fault codes
use the new namespace identifier http://www.w3.org/2001/09/soap-rpc with the
namespace prefix of rpc:. The new
codes are listed in Table 4-2.
Table 4-2. SOAP 1.2 RPC fault codes
Fault code |
Meaning |
---|---|
rpc:ProcedureNotPresent |
The server can’t find the specified procedure. |
rpc:BadArguments |
The server can’t parse the arguments (or the |
env:DataEncodingUnknown |
The |
Introduction
If you are working on integration development for Cloud Platform Integration (CPI), you have probably already used the SOAP receiver adapter with the request-reply pattern.
As long as everything works fine on the receiver side, it works fine on the CPI side, I get the SOAP response and can work with it. But in case of server errors, I noticed an undesirable behavior. SOAP Faults or error messages were not accessible for me, in the error subprocesses. The message body was simply empty, after the request-reply step.
Context
In my case, I have set up a web service call to a SOAP endpoint from SAP Process Orchestration (PO). The endpoint is configured to send an error message with details to the sender when an error occurs.
To accomplish this on SAP PO site, follow this SAP Note 2293430 – Processing error response at sender SOAP adapter – SAP ONE Support Launchpad
In the CPI I want to process these error details and display them to the responsible user. This did not work out of the box, the message text was always just empty after the error occurred.
First Attempts
The only thing I got was a generic message “Server Error” from the exchange object with the ${exception.message} operation.
Next I’ve tried a script for http error response extraction from ${property.CamelExceptionCaught} , mentioned on this help site:
Script Example for Exception Handling in HTTP Receiver – SAP Help Portal
But this script works only with HTTP exceptions, like
org.apache.camel.component.ahc.AhcOperationFailedException
It doesn’t work because we captured another object in this property during runtime. It is not AhcOperationFailedException, but an object of SoapFault class.
Adjusted Solution
I’ve stickend to the solution with a script and tried to adjust it to SOAP Fault objects.
——————————————————————–
org.apache.cxf.binding.soap
Class SoapFault extends Fault
Element |
getDetail()
Returns the detail node. |
Element |
getOrCreateDetail()
Returns the detail node. |
ref.: SoapFault (Apache CXF JavaDoc 3.2.0 API)
——————————————————————–
Those both functions provide an org.w3c.dom.Element, which is (unfortunately) not parsable by the CPI. You can set this Element as a new body and you will see the message body during trace. But operations (like XPath, Content Modifier) won’t work on your new message body. You will receive an error message like:
org.apache.camel.NoTypeConversionAvailableException: No type converter available to convert from type: com.sun.org.apache.xerces.internal.dom.ElementNSImpl to the required type: javax.xml.transform.sax.SAXSource
Serializing the org.w3c.dom.Element object to a simple String and set it as the message body, works great for the CPI.
import com.sap.gateway.ip.core.customdev.util.Message;
import java.util.HashMap;
import org.w3c.dom.Node;
import groovy.xml.*
def Message processData(Message message) {
def map = message.getProperties();
def ex = map.get("CamelExceptionCaught");
if (ex.getClass().getCanonicalName().equals("org.apache.cxf.binding.soap.SoapFault")) {
def xml = XmlUtil.serialize(ex.getOrCreateDetail());
message.setBody(xml);
}
return message;
}
– Improvements by Thomas Buerki
Conclusion
This script gives me the ability to process the SOAP fault response with the usual XPath operations and even mappings. I can now distinguish between nodes and evaluate them individually.
I hope this script helps someone who is struggling with the same problem.
I am open for improvements, opinions and questions.
Cheers
Dennis Sentler