I am creating a SOAP service using org.apache.cxf. Everything is functioning properly: data entry, processing with business logic, and returning data are all correct. However, issues arise when the entered data does not comply with the contract. For example, if a numerical field is submitted as a letter, it bypasses the validator and triggers a SOAP fault. Instead of returning a SOAP fault, I aim to return a custom object that indicates the errors. Is this achievable with this technology? I am attaching the code I am currently using. (I have attempted to extend two different classes: AbstractSoapInterceptor and AbstractPhaseInterceptor<SoapMessage>).
SoapFaultInterceptor:
package es.app.soap.interceptores;
import java.util.ArrayList;
import org.apache.cxf.binding.soap.SoapMessage;
import org.apache.cxf.binding.soap.interceptor.AbstractSoapInterceptor;
import org.apache.cxf.interceptor.Fault;
import org.apache.cxf.phase.Phase;
public class SoapFaultInterceptor extends AbstractSoapInterceptor {
public SoapFaultInterceptor() {
super(Phase.PRE_STREAM); //UNMARSHAL
//addBefore(ClientFaultConverter.class.getName());
}
@Override
public void handleMessage(SoapMessage message) throws Fault {
// TODO Auto-generated method stub
System.out.println("SoapFaultInterceptor handleMessage ");
System.out.println("SoapFaultInterceptor handleMessage END ");
}
@Override
public void handleFault(SoapMessage message) {
// TODO Auto-generated method stub
System.out.println("SoapFaultInterceptor handleFault ");
//message.setContent(Exception.class, null);
CustomSoapFaultResponse respError = new CustomSoapFaultResponse();
CustomSoapFaultResponseError error = new CustomSoapFaultResponseError();
error.setCodigo("test code");
error.setDescripcion("test description");
CustomSoapFaultResponseErrores errores = new CustomSoapFaultResponseErrores();
errores.getError().add(error);
respError.setErrores(errores);
// Elimina el SoapFault del mensaje saliente
message.setContent(CustomSoapFaultResponse.class, respError);
System.out.println("SoapFaultInterceptor handleFault END ");
}
}
CustomAbstractPhaseInterceptor:
package es.app.soap.interceptores;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import javax.xml.soap.SOAPBody;
import javax.xml.soap.SOAPException;
import javax.xml.soap.SOAPMessage;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerException;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.dom.DOMResult;
import javax.xml.transform.stream.StreamSource;
import org.apache.cxf.binding.soap.SoapMessage;
import org.apache.cxf.interceptor.Fault;
import org.apache.cxf.binding.soap.interceptor.Soap11FaultOutInterceptor;
import org.apache.cxf.phase.AbstractPhaseInterceptor;
import org.apache.cxf.phase.Phase;
import org.springframework.stereotype.Component;
public class CustomAbstractPhaseInterceptor extends AbstractPhaseInterceptor<SoapMessage>{
public CustomAbstractPhaseInterceptor() {
//super(Phase.SETUP);
super(Phase.PRE_STREAM);
//this.getBefore().add(Soap11FaultOutInterceptor.class.getName());
System.out.println("CustomAbstractPhaseInterceptor Constructor");
}
@Override
public void handleMessage(SoapMessage message) throws Fault {
// TODO Auto-generated method stub
System.out.println("CustomAbstractPhaseInterceptor handleMessage");
}
@Override
public void handleFault(SoapMessage message) {
System.out.println("CustomAbstractPhaseInterceptor handleFault");
//message.setContent(Exception.class, null);
try {
String customSoapResponse = "<Errores><Error>test error</Error></Errores>";
SOAPMessage soapMessage;
try {
soapMessage = createSoapMessage(customSoapResponse);
message.setContent(SOAPMessage.class, soapMessage);
//message.getExchange().setOutMessage(message);
} catch (TransformerException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
} catch (SOAPException | IOException e) {
throw new Fault(e);
}
//message.getInterceptorChain().pause();
}
private SOAPMessage createSoapMessage(String xml) throws SOAPException, IOException, TransformerException {
InputStream inputStream = new ByteArrayInputStream(xml.getBytes());
StreamSource streamSource = new StreamSource(inputStream);
DOMResult domResult = new DOMResult();
Transformer transformer = TransformerFactory.newInstance().newTransformer();
transformer.transform(streamSource, domResult);
// Create a SOAPMessage from the transformed DOMResult
SOAPMessage soapMessage = javax.xml.soap.MessageFactory.newInstance().createMessage();
SOAPBody soapBody = soapMessage.getSOAPBody();
soapBody.addDocument((org.w3c.dom.Document) domResult.getNode());
return soapMessage;
}
}
I have tried multiple ways that I have searched through Googling but none of them worked.
Is it possible to avoid soap fault? Whatever you do, the answer is always the same, I have attached it below:
<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
<soap:Body>
<soap:Fault>
<faultcode>soap:Client</faultcode>
<faultstring>Unmarshalling Error: cvc-length-valid: Value 'a' with length = '1' is not facet-valid with respect to length '9' for type 'NameType'.</faultstring>
</soap:Fault>
</soap:Body>
</soap:Envelope>
I hope something like this:
<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
<soap:Body>
<soap:ErrorResponse>
<Error>Unmarshalling Error: cvc-length-valid: Value 'a' with length = '1' is not facet-valid with respect to length '9' for type 'NameType'.</Error>
</soap:ErrorResponse>
</soap:Body>
</soap:Envelope>
I have tried creating a soap message from scratch, I am not able to. I have tried deleting the Fault, and creating a custom message, but it didnt work either.