0
votes

Spring Boot Apache CXF capture les erreurs de savon et renvoie le code d'erreur http

J'utilise Spring Boot avec Apache CXF. Le démarreur que cxf-spring-boot-starter-jaxws est la botte à ressort cxf-spring-boot-starter-jaxws . J'ai suivi les guides du site Web d'Apaches cxf. http://cxf.apache.org/docs/springboot.html .

J'utilise également Java11.

Le problème que je rencontre est que le savon demande à l'application que l'erreur renvoie toujours 200 en-têtes, qu'il y ait eu une erreur ou non.

Le point de terminaison du service Web que je frappe est une méthode vide, mais je voudrais toujours renvoyer un code 500 dans l'en-tête http si cela ne fonctionne pas.

La stratégie que j'ai à l'esprit est de créer un intercepteur qui interceptera les demandes de savon cette erreur et modifiera la réponse finale avec un code d'en-tête http. Ainsi, le corps serait nul pour les méthodes void mais l'en-tête est 400 ou 500.

Ma configuration de service de savon ressemble à ceci

Interceptor for {http://xyz/}SoapEventEndpointService#{http:/xyz}ReceiveEvent has thrown exception, unwinding now

org.apache.cxf.interceptor.Fault: Unmarshalling Error: unexpected element (uri:"http://xyz", local:"EventId1"). Expected elements are <{http://xyz}LocalAssessmentId>,<{http://xyz}LOTId>,<{http://xyz}EventId>,<{http://xyz}Benefit> 
    at org.apache.cxf.jaxb.JAXBEncoderDecoder.unmarshall(JAXBEncoderDecoder.java:938) ~[cxf-rt-databinding-jaxb-3.3.1.jar:3.3.1]
    at org.apache.cxf.jaxb.JAXBEncoderDecoder.unmarshall(JAXBEncoderDecoder.java:744) ~[cxf-rt-databinding-jaxb-3.3.1.jar:3.3.1]
    at org.apache.cxf.jaxb.io.DataReaderImpl.read(DataReaderImpl.java:172) ~[cxf-rt-databinding-jaxb-3.3.1.jar:3.3.1]
    at org.apache.cxf.wsdl.interceptors.DocLiteralInInterceptor.handleMessage(DocLiteralInInterceptor.java:194) ~[cxf-rt-wsdl-3.3.1.jar:3.3.1]
    at org.apache.cxf.phase.PhaseInterceptorChain.doIntercept(PhaseInterceptorChain.java:308) ~[cxf-core-3.3.1.jar:3.3.1]
    at org.apache.cxf.transport.ChainInitiationObserver.onMessage(ChainInitiationObserver.java:121) ~[cxf-core-3.3.1.jar:3.3.1]
    at org.apache.cxf.transport.http.AbstractHTTPDestination.invoke(AbstractHTTPDestination.java:267) ~[cxf-rt-transports-http-3.3.1.jar:3.3.1]
    at org.apache.cxf.transport.servlet.ServletController.invokeDestination(ServletController.java:234) ~[cxf-rt-transports-http-3.3.1.jar:3.3.1]
    at org.apache.cxf.transport.servlet.ServletController.invoke(ServletController.java:208) ~[cxf-rt-transports-http-3.3.1.jar:3.3.1]
    at org.apache.cxf.transport.servlet.ServletController.invoke(ServletController.java:160) ~[cxf-rt-transports-http-3.3.1.jar:3.3.1]
    at org.apache.cxf.transport.servlet.CXFNonSpringServlet.invoke(CXFNonSpringServlet.java:216) ~[cxf-rt-transports-http-3.3.1.jar:3.3.1]
    at org.apache.cxf.transport.servlet.AbstractHTTPServlet.handleRequest(AbstractHTTPServlet.java:301) ~[cxf-rt-transports-http-3.3.1.jar:3.3.1]
    at org.apache.cxf.transport.servlet.AbstractHTTPServlet.doPost(AbstractHTTPServlet.java:220) ~[cxf-rt-transports-http-3.3.1.jar:3.3.1]
    at javax.servlet.http.HttpServlet.service(HttpServlet.java:660) ~[tomcat-embed-core-9.0.16.jar:9.0.16]
    at org.apache.cxf.transport.servlet.AbstractHTTPServlet.service(AbstractHTTPServlet.java:276) ~[cxf-rt-transports-http-3.3.1.jar:3.3.1]
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:231) ~[tomcat-embed-core-9.0.16.jar:9.0.16]
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166) ~[tomcat-embed-core-9.0.16.jar:9.0.16]
    at org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:53) ~[tomcat-embed-websocket-9.0.16.jar:9.0.16]
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193) ~[tomcat-embed-core-9.0.16.jar:9.0.16]
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166) ~[tomcat-embed-core-9.0.16.jar:9.0.16]
    at org.springframework.web.filter.RequestContextFilter.doFilterInternal(RequestContextFilter.java:99) ~[spring-web-5.1.5.RELEASE.jar:5.1.5.RELEASE]
    at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107) ~[spring-web-5.1.5.RELEASE.jar:5.1.5.RELEASE]
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193) ~[tomcat-embed-core-9.0.16.jar:9.0.16]
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166) ~[tomcat-embed-core-9.0.16.jar:9.0.16]
    at org.springframework.web.filter.FormContentFilter.doFilterInternal(FormContentFilter.java:92) ~[spring-web-5.1.5.RELEASE.jar:5.1.5.RELEASE]
    at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107) ~[spring-web-5.1.5.RELEASE.jar:5.1.5.RELEASE]
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193) ~[tomcat-embed-core-9.0.16.jar:9.0.16]
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166) ~[tomcat-embed-core-9.0.16.jar:9.0.16]
    at org.springframework.web.filter.HiddenHttpMethodFilter.doFilterInternal(HiddenHttpMethodFilter.java:93) ~[spring-web-5.1.5.RELEASE.jar:5.1.5.RELEASE]
    at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107) ~[spring-web-5.1.5.RELEASE.jar:5.1.5.RELEASE]
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193) ~[tomcat-embed-core-9.0.16.jar:9.0.16]
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166) ~[tomcat-embed-core-9.0.16.jar:9.0.16]
    at org.springframework.web.filter.CharacterEncodingFilter.doFilterInternal(CharacterEncodingFilter.java:200) ~[spring-web-5.1.5.RELEASE.jar:5.1.5.RELEASE]
    at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107) ~[spring-web-5.1.5.RELEASE.jar:5.1.5.RELEASE]
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193) ~[tomcat-embed-core-9.0.16.jar:9.0.16]
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166) ~[tomcat-embed-core-9.0.16.jar:9.0.16]
    at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:200) ~[tomcat-embed-core-9.0.16.jar:9.0.16]
    at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:96) ~[tomcat-embed-core-9.0.16.jar:9.0.16]
    at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:490) ~[tomcat-embed-core-9.0.16.jar:9.0.16]
    at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:139) ~[tomcat-embed-core-9.0.16.jar:9.0.16]
    at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:92) ~[tomcat-embed-core-9.0.16.jar:9.0.16]
    at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:74) ~[tomcat-embed-core-9.0.16.jar:9.0.16]
    at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:343) ~[tomcat-embed-core-9.0.16.jar:9.0.16]
    at org.apache.coyote.http11.Http11Processor.service(Http11Processor.java:408) ~[tomcat-embed-core-9.0.16.jar:9.0.16]
    at org.apache.coyote.AbstractProcessorLight.process(AbstractProcessorLight.java:66) ~[tomcat-embed-core-9.0.16.jar:9.0.16]
    at org.apache.coyote.AbstractProtocol$ConnectionHandler.process(AbstractProtocol.java:834) ~[tomcat-embed-core-9.0.16.jar:9.0.16]
    at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1415) ~[tomcat-embed-core-9.0.16.jar:9.0.16]
    at org.apache.tomcat.util.net.SocketProcessorBase.run(SocketProcessorBase.java:49) ~[tomcat-embed-core-9.0.16.jar:9.0.16]
    at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1128) ~[na:na]
    at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:628) ~[na:na]
    at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61) ~[tomcat-embed-core-9.0.16.jar:9.0.16]
    at java.base/java.lang.Thread.run(Thread.java:834) ~[na:na]
Caused by: javax.xml.bind.UnmarshalException: null
    at com.sun.xml.bind.v2.runtime.unmarshaller.UnmarshallerImpl.handleStreamException(UnmarshallerImpl.java:483) ~[jaxb-runtime-2.3.1.jar:2.3.1]
    at com.sun.xml.bind.v2.runtime.unmarshaller.UnmarshallerImpl.unmarshal0(UnmarshallerImpl.java:417) ~[jaxb-runtime-2.3.1.jar:2.3.1]
    at com.sun.xml.bind.v2.runtime.unmarshaller.UnmarshallerImpl.unmarshal(UnmarshallerImpl.java:394) ~[jaxb-runtime-2.3.1.jar:2.3.1]
    at org.apache.cxf.jaxb.JAXBEncoderDecoder.doUnmarshal(JAXBEncoderDecoder.java:887) ~[cxf-rt-databinding-jaxb-3.3.1.jar:3.3.1]
    at org.apache.cxf.jaxb.JAXBEncoderDecoder.access$200(JAXBEncoderDecoder.java:103) ~[cxf-rt-databinding-jaxb-3.3.1.jar:3.3.1]
    at org.apache.cxf.jaxb.JAXBEncoderDecoder$3.run(JAXBEncoderDecoder.java:926) ~[cxf-rt-databinding-jaxb-3.3.1.jar:3.3.1]
    at java.base/java.security.AccessController.doPrivileged(Native Method) ~[na:na]
    at org.apache.cxf.jaxb.JAXBEncoderDecoder.unmarshall(JAXBEncoderDecoder.java:924) ~[cxf-rt-databinding-jaxb-3.3.1.jar:3.3.1]
    ... 51 common frames omitted
Caused by: com.sun.istack.SAXParseException2: unexpected element (uri:"xyz", local:"EventId1"). Expected elements are <{http://xyz}LocalAssessmentId>,<{http://xyz}LOTId>,<{http://xyz}EventId>,<{http://xyz}Benefit>
    at com.sun.xml.bind.v2.runtime.unmarshaller.UnmarshallingContext.handleEvent(UnmarshallingContext.java:744) ~[jaxb-runtime-2.3.1.jar:2.3.1]
    at com.sun.xml.bind.v2.runtime.unmarshaller.Loader.reportError(Loader.java:262) ~[jaxb-runtime-2.3.1.jar:2.3.1]
    at com.sun.xml.bind.v2.runtime.unmarshaller.Loader.reportError(Loader.java:257) ~[jaxb-runtime-2.3.1.jar:2.3.1]
    at com.sun.xml.bind.v2.runtime.unmarshaller.Loader.reportUnexpectedChildElement(Loader.java:124) ~[jaxb-runtime-2.3.1.jar:2.3.1]
    at com.sun.xml.bind.v2.runtime.unmarshaller.Loader.childElement(Loader.java:105) ~[jaxb-runtime-2.3.1.jar:2.3.1]
    at com.sun.xml.bind.v2.runtime.unmarshaller.StructureLoader.childElement(StructureLoader.java:268) ~[jaxb-runtime-2.3.1.jar:2.3.1]
    at com.sun.xml.bind.v2.runtime.unmarshaller.UnmarshallingContext._startElement(UnmarshallingContext.java:574) ~[jaxb-runtime-2.3.1.jar:2.3.1]
    at com.sun.xml.bind.v2.runtime.unmarshaller.UnmarshallingContext.startElement(UnmarshallingContext.java:556) ~[jaxb-runtime-2.3.1.jar:2.3.1]
    at com.sun.xml.bind.v2.runtime.unmarshaller.StAXStreamConnector.handleStartElement(StAXStreamConnector.java:246) ~[jaxb-runtime-2.3.1.jar:2.3.1]
    at com.sun.xml.bind.v2.runtime.unmarshaller.StAXStreamConnector.bridge(StAXStreamConnector.java:180) ~[jaxb-runtime-2.3.1.jar:2.3.1]
    at com.sun.xml.bind.v2.runtime.unmarshaller.UnmarshallerImpl.unmarshal0(UnmarshallerImpl.java:415) ~[jaxb-runtime-2.3.1.jar:2.3.1]
    ... 57 common frames omitted
Caused by: javax.xml.bind.UnmarshalException: unexpected element (uri:"http://uxyz", local:"EventId1"). Expected elements are <{http://xyz}LocalAssessmentId>,<{http://xyz}LOTId>,<{http://xyz}EventId>,<{http://uxyz}Benefit>
    ... 68 common frames omitted

2019-03-27 15:44:15.245 DEBUG 22552 --- [nio-8080-exec-4] o.a.c.t.http.AbstractHTTPDestination     : Finished servicing http request on thread: Thread[http-nio-8080-exec-4,5,main]
2019-03-27 15:44:15.245 DEBUG 22552 --- [nio-8080-exec-4] o.a.c.t.servlet.ServletController        : Finished servicing http request on thread: Thread[http-nio-8080-exec-4,5,main]

Intercepteurs

@WebService(targetNamespace = "xyz", name = "ReceiveEventEndpoint")
@XmlSeeAlso({ObjectFactory.class})
@SOAPBinding(parameterStyle = SOAPBinding.ParameterStyle.BARE)
public interface ReceiveEventEndpoint {

    @WebMethod(operationName = "ReceiveEvent", action = "xyz")
    @Oneway
    public void receiveEvent(
        @WebParam(partName = "in", name = "ReceiveEventRequest", targetNamespace = "xyz")
        ReceiveEventRequest in
    );
}

WSDL vers point de terminaison d'événement généré par Java

@Slf4j
public class SoapFaultInterceptor extends AbstractSoapInterceptor {

    public SoapFaultInterceptor(String phase) {
        super(phase);
    }

    @Override
    public void handleMessage(SoapMessage message) throws Fault {
        log.info("THIS NEVER GETS CALLED!!  Execpting errors to invoke this method");
    }
}

@Slf4j
public class SoapInterceptor extends AbstractSoapInterceptor {


    public SoapInterceptor(String p){
        super(p);
    }

    @Override
    public void handleMessage(SoapMessage message) throws Fault {
        log.info("THIS WORKS");
    }
}

1) Lorsque j'invalide avec le XML et envoie la demande. Seul le SoapInterceptor intercepte? Le SoapFaultInterceptor pas? Aucune suggestion?

@Configuration
@Slf4j
public class WebServiceConfiguration {

    @Autowired
    private Bus bus;

    @Bean
    public Endpoint endpoint() {
        EndpointImpl endpoint = new EndpointImpl(bus, new SoapEventEndpoint());
        endpoint.publish("/EventEndpoint");
        endpoint.setWsdlLocation("ReceiveEvent.wsdl");

        // this interceptor works
        endpoint.getInInterceptors().add(soapInterceptor());

        // these two fault interceptors do not work!
        endpoint.getInFaultInterceptors().add(soapFaultInterceptor());
        endpoint.getOutFaultInterceptors().add(soapFaultInterceptor());

        return endpoint;
    }

    @Bean
    public AbstractSoapInterceptor soapFaultInterceptor(){
        return new SoapFaultInterceptor( Phase.PRE_STREAM);
    }

    @Bean
    public AbstractSoapInterceptor soapInterceptor(){
        return new SoapInterceptor( Phase.PRE_STREAM);
    }
}

** 2) Comment puis-je ajouter un intercepteur (ou un écouteur de défauts) ou une autre méthode pour mettre à jour l'en-tête http après l'interception des erreurs?


3 commentaires

Pouvez-vous essayer de remplacer handleFault ()?


bon cri ... j'ai essayé ça et ça n'a pas marché


Donc, la première partie fonctionne maintenant ... J'avais juste besoin d'ajouter le handleFault comme vous l'avez suggéré. Cependant, il devait être injecté avant la phase de marshal, pas après ... Je suppose que le prochain défi à résoudre est de savoir comment ajouter un en-tête http


3 Réponses :


0
votes

Avez-vous essayé quelque chose comme ceci:

@Override
public void handleFault(Message message) {
    Map<String, List> headers = (Map<String, List>) message.get(Message.PROTOCOL_HEADERS);
    try {
        headers.put("Status", "500");
        ...
    } catch (Exception ce) {
        ...
    }
}


0 commentaires

-2
votes

Ce qui suit a fonctionné pour moi: dans la classe package-info.java dans project, j'ai changé:

elementFormDefault = javax.xml.bind.annotation.XmlNsForm.UNQUALIFIED


0 commentaires

0
votes

Dans l'interface ReceiveEventEndpoint , la méthode receiveEvent est annotée avec l'annotation @OneWay qui signifie qu'elle n'a qu'un message d'entrée et aucune sortie (y compris les défauts).

Vous pouvez trouver plus de détails ici Comprendre l'annotation @Oneway dans JAX-WS , http://cxf.547215.n5.nabble.com/Detecting-unmarshalling-error-with-Oneway-operations-td5742536.html et http: // cxf.547215.n5.nabble.com/OneWay-interpretation-and-HTTP-binding-td5711719.html

Pour des raisons de débogage, si vous devez lever des erreurs dans les opérations OneWay, vous pouvez ajouter la propriété Message.ROBUST_ONEWAY à votre point de terminaison:

<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
   <soap:Body>
      <soap:Fault>
         <faultcode>soap:Client</faultcode>
         <faultstring>Unmarshalling Error: unexpected element (uri:"http://xyz", local:"EventId1")...</faultstring>
      </soap:Fault>
   </soap:Body>
</soap:Envelope>

Pour les demandes non valides, vous obtiendrez un HTTP 500 avec le corps suivant sans avoir besoin d'ajouter un intercepteur personnalisé:

@Bean
public Endpoint endpoint() {
    EndpointImpl endpoint = new EndpointImpl(bus, new SoapEventEndpoint());
    endpoint.publish("/EventEndpoint");
    endpoint.setWsdlLocation("ReceiveEvent.wsdl");

    // interceptors...

    //To throw exceptions in OneWay messages
    Map<String, Object> prop = new HashMap<>();
    prop.put(Message.ROBUST_ONEWAY, true);
    endpoint.setProperties(prop);

    return endpoint;
}


0 commentaires