Faults
A Web service returns a fault message to the client to report an error. SOAP 1.1 allows a server to distinguish between faults that result from incorrect input, client faults, and faults that originate in the server, server faults.
Client Faults
The client proxy may throw:
• rwsf::ServerFault when an internal error in the proxy occurs before the operation is invoked.
• rwsf::SoapFaultException when the server returns a fault that is not specified as a possible exception in the WSDL file.
• Custom generated faults derived from
rwsf::Fault. (See section for WSDL fault generation)
• Exception types that maybe generated by data mapping mechanism to handle the data types on the response message.
Handling Returned Faults from Client Proxies
For instances of both
rwsf::ClientFault and
rwsf::ServerFault, the
rwsf::Fault.getFaultString() method retrieves the SOAP fault, while
rwsf::Fault.getFaultCode() method returns from the server an enum
rwsf:: FaultCode. The
FaultCode defines whether this is a client fault or a server fault.
Server Faults
On the wire, a SOAP 1.1 server returns all internal exceptions, or server faults, either as a client or server SOAP fault message. When a request is invalid, the server returns a client SOAP fault message. If a request results in a SOAP client fault, sending the same request again will result in another SOAP client fault. By contrast, a server returns a SOAP server fault to indicate a problem unrelated to the content of the request. If the application sends the same request again, the request may succeed. Service endpoint implementations may generate a server fault and will generate either a SOAP client or SOAP server fault depending on the type of exception thrown.
You can detect any errors you want to watch for in your service and return them to the client as an instance of one of the defined fault messages in the WSDL. Simply construct the desired fault message type instance and throw it. The fault is caught in the message handler, which constructs a SOAP server or SOAP client fault message and sends it to the client.
Here is an example that causes the server exception to generate a SOAP server fault message:
throw AFaultMessage_message("This is the explanation of the fault",
rwsf::XmlName("Server",rwsf::XmlNamespace("SOAP-ENV",
"http://schemas.xmlsoap.org/soap/envelope/")),
"Error: some kind of fault");
The
rwsf::XmlName parameter inserts a SOAP server fault code in the SOAP fault message. To construct a standard SOAP server fault message, define the parameter exactly as shown here. The first string parameter becomes the detail entry of the SOAP fault message, and the last string parameter becomes the human-readable fault string entry.
This example throws an exception resulting from invalid client input, generating a SOAP client fault message.
rwsf::Decimal
AccountServlet::getAccountBalance(const std::string& user,
const std::string& password)
{
try {
Account acct(user, password);
return acct.getBalance();
}
catch (const AccountInvalidLoginError& e) {
throw rwsf::ClientFault(e.what());
}
}
Note that the sample translates a specific exception type to a SOAP client fault on the wire. If an exception is not handled in the server operation, it is propagated as a SOAP server fault on the wire.
WSDL-Defined Faults
HydraExpress generates code to facilitate fault handling for any faults defined in the WSDL file. HydraExpress generates a class for each defined fault message element, with the name of the class being the message name with the string _message appended: message-name_message.
For example, for this WSDL defined fault message:
<operation name="faultMethod" parameterOrder="in1">
<input message="tns:faultMethodRequest"/>
<output message="tns:faultMethodResponse"/>
<fault name="InvalidRequest" message="tns:InvalidRequest"/>
<fault name="WrongParameter" message="tns:WrongParameter"/>
</operation>
The generated class is
InvalidRequest_message, deriving from the HydraExpress class
rwsf::Fault. Each class contains the following datatypes, with corresponding accessor methods inherited from
rwsf::Fault:
• A fault code, corresponding to the code element required in SOAP fault messages. Accessor methods are getFaultCode() and getFaultCodeAsString().
• a fault string, corresponding to the human-readable string element required in SOAP fault messages. Accessor method is getFaultString().
• one or more message parts, corresponding to the parts specified for this fault message type in the WSDL. These message parts become entries in the detail element of the SOAP message. Accessor methods are get<part-name>() where part-name is the name of the WSDL message part that adds additional detail for the fault message.
Generated Client-Side Code
If the WSDL defines fault messages, the generated client-side sample application includes catch blocks for each operation, with one block for each defined message element.
To better understand this connection, let’s look at how a fault gets defined in the WSDL. First, the WSDL defines one or more message elements to be used by the fault:
For instance, the following WSDL excerpt contains a message that will be used by the fault, InvalidRequest. (Note that the WSDL would also require a <types> element to define the complex type tns:InvalidRequest, but this is not shown.)
<message name="faultMethodRequest">
<part name="in1" type="xsd:int"/>
</message>
<message name="faultMethodResponse">
<part name="return" type="xsd:int"/>
</message>
<message name="InvalidRequest">
Then the message element is used in defining a fault in the portType definition:
<portType name="Fault">
<operation name="faultMethod" parameterOrder="in1">
<input message="tns:faultMethodRequest"/>
<output message="tns:faultMethodResponse"/>
<fault name="InvalidRequest" message="tns:InvalidRequest"/>
</operation>
</portType>
Finally, the portType fault definition is used in the binding to define a SOAP fault:
<binding name="Fault" type="tns:Fault">
<soap:binding style="rpc" transport="http://schemas.xmlsoap.org/soap/http"/>
<operation name="faultMethod">
<soap:operation soapAction="faultMethod" style="rpc"/>
<input>
<soap:body use="literal"
namespace="http://www.roguewave.com/rwsf/webservice/"/>
</input>
<output>
<soap:body use="literal"
namespace="http://www.roguewave.com/rwsf/webservice/"/>
</output>
<fault name="InvalidRequest">
<soap:fault name="InvalidRequest" use="literal"/>
</operation>
</binding>
The generated client sample application would then include the following code to catch and display the fault message. For instance, here’s the code that would handle the InvalidRequest fault:
try {
rwsf::CallInfo info;
return_ret = proxy.faultMethod(info, data);
}
catch(const InvalidRequest_message& e) {
std::cout << "Fault Code: " << e.getFaultCodeAsString()
<< std::endl;
std::cout << "Fault String: " << e.getFaultString()
<< std::endl;
int data = e.getrequestInfo().getData();
std::cout << "data = " << data << std::endl;
// Use the string message data in whatever way you need...
}
// Catch blocks for other WSDL-defined message types.
// ...
// In the proxy implementation, these blocks catch
// other general exceptions
catch (const rwsf::Exception& x) {
std::cerr << "Error invoking web service: "
<< x.what() << std::endl;
} catch(std::exception& e) {
std::cerr << "Error invoking web service: "
<< e.what() << std::endl;
return 1;
} catch(...) {
std::cerr << "Unknown exception thrown" << std::endl;
}
As you can see, the generated code does not attempt to guess how the detail entry of the SOAP message needs to be handled. This entry can be anything from a simple string to a complex data element, so the service developer is the person in the best position to know how it must be handled.