简体   繁体   English

Spring Web 服务 - 异常跳过 ExceptionResolver

[英]Spring Web Services - Exception Skipping ExceptionResolver

I have a SOAP service, the request and responses work as expected with good input, if I specify bad input for an XML element我有一个 SOAP 服务,如果我为 XML 元素指定了错误的输入,请求和响应将按预期工作并具有良好的输入

in request body:在请求正文中:

...
<ns:myIntegerElement>asdf</ns:myIntegerElement>
...

my exception resolver is invoked, this resolver is just an implementation of the exception resolver, so it doesn't have exception mappings, just a few System.out's in the abstract method我的异常解析器被调用了,这个解析器只是异常解析器的一个实现,所以它没有异常映射,只是抽象方法中的几个System.out

<bean id="exceptionResolver" class="com.mycompany.ws.MyExceptionResolver">

however, if I send a request that looks more like this:但是,如果我发送一个看起来更像这样的请求:

...
    <ns:myIntegSOMETHINGGOTTOTALLYMESSUP!!!ent>asdf</ns:myIntegerElement>
...

my resolver isn't executed at all我的解析器根本没有执行

I setup log4j to have a root debug level and see this output:我将 log4j 设置为具有根调试级别并查看此输出:

2010-08-09 10:30:01,900 [Thread:http-8080-2] DEBUG [org.springframework.ws.transport.http.WebServiceMessageReceiverHandlerAdapter] - Accepting incoming [org.springframework.ws.transport.http.HttpServletConnection@c46dcf] to [ http://localhost:8080/myws/MyWebServices/] ERROR: 'The element type "ns:MESSEDUPELEMENT" must be terminated by the matching end-tag "".' 2010-08-09 10:30:01,900 [线程:http-8080-2] 调试 [org.springframework.ws.transport.http.WebServiceMessageReceiverHandlerAdapter] - 接受传入 [org.springframework.ws.transport.http.HttpServletConnection@c46dcf ] 到 [ http://localhost:8080/myws/MyWebServices/]错误:'元素类型“ns:MESSEDUPELEMENT”必须由匹配的结束标记“”终止。' 2010-08-09 10:30:01,920 [Thread:http-8080-2] DEBUG [org.springframework.ws.transport.http.MessageDispatcherServlet] - Could not complete request org.springframework.ws.soap.saaj.SaajSoapMessageException: Could not access envelope: Unable to create envelope from given source: ; 2010-08-09 10:30:01,920 [线程:http-8080-2] 调试 [org.springframework.ws.transport.http.MessageDispatcherServlet] - 无法完成请求 org.springframework.ws.soap.saaj.SaajSoapMessageException:无法访问信封:无法从给定来源创建信封:; nested exception is com.sun.xml.internal.messaging.saaj.SOAPExceptionImpl: Unable to create envelope from given source: at org.springframework.ws.soap.saaj.support.SaajUtils.getSaajVersion(SaajUtils.java:162) at org.springframework.ws.soap.saaj.SaajSoapMessage.getImplementation(SaajSoapMessage.java:251) at org.springframework.ws.soap.saaj.SaajSoapMessage.(SaajSoapMessage.java:84) at org.springframework.ws.soap.saaj.SaajSoapMessage.(SaajSoapMessage.java:70) at org.springframework.ws.soap.saaj.SaajSoapMessageFactory.createWebServiceMessage(SaajSoapMessageFactory.java:168) at org.springframework.ws.transport.AbstractWebServiceConnection.receive(AbstractWebServiceConnection.java:90) at org.springframework.ws.transport.support.WebServiceMessageReceiverObjectSupport.handleConnection(WebServiceMessageReceiverObjectSupport.java:86) at org.springframework.ws.transport.http.WebServiceMessageReceiverHandlerAdapter.handle(WebServiceMessageReceiverHandlerAdapter.java:57) at org.springframework.ws.t嵌套异常是 com.sun.xml.internal.messaging.saaj.SOAPExceptionImpl:无法从给定源创建信封:在 org.springframework.ws.soap.saaj.support.SaajUtils.getSaajVersion(SaajUtils.java:162) 在 org .springframework.ws.soap.saaj.SaajSoapMessage.getImplementation(SaajSoapMessage.java:251) 在 org.springframework.ws.soap.saaj.SaajSoapMessage.(SaajSoapMessage.java:84) 在 org.springframework.ws.soap.saaj。 SaajSoapMessage.(SaajSoapMessage.java:70) 在 org.springframework.ws.soap.saaj.SaajSoapMessageFactory.createWebServiceMessage(SaajSoapMessageFactory.java:168) 在 org.springframework.ws.transport.AbstractWebServiceConnection.receive(AbstractWebServiceConnection.java:90) org.springframework.ws.transport.support.WebServiceMessageReceiverObjectSupport.handleConnection(WebServiceMessageReceiverObjectSupport.java:86)在org.springframework.ws.transport.http.WebServiceMessageReceiverHandlerAdapter.handle(WebServiceMessageReceiverHandlerAdapter.java:57)在org.springframework.ws.t ransport.http.MessageDispatcherServlet.doService(MessageDispatcherServlet.java:230) at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:571) at org.springframework.web.servlet.FrameworkServlet.doPost(FrameworkServlet.java:511) at javax.servlet.http.HttpServlet.service(HttpServlet.java:637) at javax.servlet.http.HttpServlet.service(HttpServlet.java:717) at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:290) at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:206) at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:233) at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:191) at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:127) at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:102) at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.jav ransport.http.MessageDispatcherServlet.doService(MessageDispatcherServlet.java:230) 在 org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:571) 在 org.springframework.web.servlet.FrameworkServlet.doPost(FrameworkServlet.java: 511) 在 javax.servlet.http.HttpServlet.service(HttpServlet.java:637) .java:290) 在 org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:206) 在 org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:233) 在 org.apache.catalina。 core.StandardContextValve.invoke(StandardContextValve.java:191) 在 org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:127) 在 org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:102)在 org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.jav a:109) at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:298) at org.apache.coyote.http11.Http11AprProcessor.process(Http11AprProcessor.java:859) at org.apache.coyote.http11.Http11AprProtocol$Http11ConnectionHandler.process(Http11AprProtocol.java:579) at org.apache.tomcat.util.net.AprEndpoint$Worker.run(AprEndpoint.java:1555) at java.lang.Thread.run(Thread.java:619) Caused by: com.sun.xml.internal.messaging.saaj.SOAPExceptionImpl: Unable to create envelope from given source: at com.sun.xml.internal.messaging.saaj.soap.EnvelopeFactory.createEnvelope(EnvelopeFactory.java:114) at com.sun.xml.internal.messaging.saaj.soap.ver1_1.SOAPPart1_1Impl.createEnvelopeFromSource(SOAPPart1_1Impl.java:70) at com.sun.xml.internal.messaging.saaj.soap.SOAPPartImpl.getEnvelope(SOAPPartImpl.java:122) at org.springframework.ws.soap.saaj.support.SaajUtils.getSaajVersion(SaajUtils.java:159)... 24 more Caused by: javax.xml.transform.TransformerException: org.xml.sax.SAXParseException: Th a:109) 在 org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:298) 在 org.apache.coyote.http11.Http11AprProcessor.process(Http11AprProcessor.java:859) 在 org.apache.coyote.http11 .Http11AprProtocol$Http11ConnectionHandler.process(Http11AprProtocol.java:579) 在 org.apache.tomcat.util.net.AprEndpoint$Worker.run(AprEndpoint.java:1555) 在 java.lang.Thread.run(Thread.java:619) ) 原因:com.sun.xml.internal.messaging.saaj.SOAPExceptionImpl:无法从给定源创建信封:在 com.sun.xml.internal.messaging.saaj.soap.EnvelopeFactory.createEnvelope(EnvelopeFactory.java:114 ) 在 com.sun.xml.internal.messaging.saaj.soap.ver1_1.SOAPPart1_1Impl.createEnvelopeFromSource(SOAPPart1_1Impl.java:70) 在 com.sun.xml.internal.messaging.saaj.soap.SOAPPartImpl.getEnvelope(SOAPPartImpl.java :122) at org.springframework.ws.soap.saaj.support.SaajUtils.getSaajVersion(SaajUtils.java:159)... 24 更多 由:javax.xml.transform.TransformerException: org.xml.sax.SAXParseException:日e element type "smm:smm-aid" must be terminated by the matching end-tag "".元素类型“smm:smm-aid”必须以匹配的结束标记“”结束。 at com.sun.org.apache.xalan.internal.xsltc.trax.TransformerImpl.transform(TransformerImpl.java:719) at com.sun.org.apache.xalan.internal.xsltc.trax.TransformerImpl.transform(TransformerImpl.java:313) at com.sun.xml.internal.messaging.saaj.util.transform.EfficientStreamingTransformer.transform(EfficientStreamingTransformer.java:393) at com.sun.xml.internal.messaging.saaj.soap.EnvelopeFactory.createEnvelope(EnvelopeFactory.java:102)... 27 more Caused by: org.xml.sax.SAXParseException: The element type "smm:smm-aid" must be terminated by the matching end-tag "".在 com.sun.org.apache.xalan.internal.xsltc.trax.TransformerImpl.transform(TransformerImpl.java:719) 在 com.sun.org.apache.xalan.internal.xsltc.trax.TransformerImpl.transform(TransformerImpl. java:313) 在 com.sun.xml.internal.messaging.saaj.util.transform.EfficientStreamingTransformer.transform(EfficientStreamingTransformer.java:393) 在 com.sun.xml.internal.messaging.saaj.soap.EnvelopeFactory.createEnvelope( EnvelopeFactory.java:102)... 27 更多 原因:org.xml.sax.SAXParseException:元素类型“smm:smm-aid”必须由匹配的结束标记“”终止。 at com.sun.org.apache.xerces.internal.parsers.AbstractSAXParser.parse(AbstractSAXParser.java:1231) at com.sun.org.apache.xerces.internal.jaxp.SAXParserImpl$JAXPSAXParser.parse(SAXParserImpl.java:522) at org.xml.sax.helpers.XMLFilterImpl.parse(XMLFilterImpl.java:333) at com.sun.org.apache.xalan.internal.xsltc.trax.TransformerImpl.transformIdentity(TransformerImpl.java:636) at com.sun.org.apache.xalan.internal.xsltc.trax.TransformerImpl.transform(TransformerImpl.java:707)... 30 more在 com.sun.org.apache.xerces.internal.parsers.AbstractSAXParser.parse(AbstractSAXParser.java:1231) 在 com.sun.org.apache.xerces.internal.jaxp.SAXParserImpl$JAXPSAXParser.parse(SAXParserImpl.java: 522) 在 org.xml.sax.helpers.XMLFilterImpl.parse(XMLFilterImpl.java:333) 在 com.sun.org.apache.xalan.internal.xsltc.trax.TransformerImpl.transformIdentity(TransformerImpl.java:636) 在 com .sun.org.apache.xalan.internal.xsltc.trax.TransformerImpl.transform(TransformerImpl.java:707)... 30 多个

It appears that spring is missing a possible exception here, and not wrapping it, but such a basic error condition not being caught seems unlikely to me.似乎 spring 在这里缺少一个可能的异常,而不是包装它,但在我看来,这样一个没有被捕获的基本错误条件似乎不太可能。 Can anyone help me to find the root of this issue?谁能帮我找到这个问题的根源?

I'll include web.xml and servlet.xml too:我还将包含 web.xml 和 servlet.xml:

web.xml:网站.xml:

<?xml version="1.0" encoding="UTF-8"?>
<web-app version="2.5" xmlns="http://java.sun.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd">
    <servlet>
        <servlet-name>ws</servlet-name>
        <servlet-class>org.springframework.ws.transport.http.MessageDispatcherServlet</servlet-class>
        <load-on-startup>1</load-on-startup>
        <init-param>
            <param-name>transformWsdlLocations</param-name>
            <param-value>true</param-value>
        </init-param>
    </servlet>
    <servlet-mapping>
        <servlet-name>ws</servlet-name>
        <url-pattern>/*</url-pattern>
    </servlet-mapping>
</web-app>

servlet context:小服务程序上下文:

<context:component-scan base-package="com.mycomp.proj.ws" />
    <bean id="smmService" class="com.mycomp.proj.ws.SMMRequestHandlingServiceStubImpl"/>

    <bean class="org.springframework.ws.server.endpoint.mapping.PayloadRootAnnotationMethodEndpointMapping"/>
    <bean class="org.springframework.ws.server.endpoint.adapter.MarshallingMethodEndpointAdapter">
        <constructor-arg ref="marshaller"/>
    </bean>
    <bean id="marshaller" class="org.springframework.oxm.castor.CastorMarshaller">
        <property name="mappingLocations">
            <list>
                <value>classpath:mapping.xml</value>
                <value>classpath:hoursOfOperationMapping.xml</value>
            </list>
        </property>
    </bean>

    <bean id="smmws" class="org.springframework.ws.wsdl.wsdl11.DefaultWsdl11Definition">
        <property name="schema" ref="schema" />
        <property name="portTypeName" value="SMM" />
        <property name="locationUri" value="/SMMWebServices/"/>
        <property name="targetNamespace" value="http://mycomp.proj.com" />
    </bean>

    <bean id="exceptionResolver" class="com.wdp.smm.ws.MyExceptionResolver"/>

    <bean id="schema" class="org.springframework.xml.xsd.SimpleXsdSchema">
        <property name="xsd" value="/WEB-INF/ws.xsd" />
    </bean>

The MessageDispatcherServlet creates its own two instances of EnpointExceptionResolver by default, namely SoapFaultAnnotationExceptionResolver@1 and SimpleSoapExceptionResolver@2 MessageDispatcherServlet 默认创建自己的两个 EnpointExceptionResolver 实例,分别是 SoapFaultAnnotationExceptionResolver@1 和 SimpleSoapExceptionResolver@2

And when it resolves an exception the SimpleSoapExceptionResolver@2 will stop any other registered EnpointExceptionResolver to handle an exception.当它解决异常时,SimpleSoapExceptionResolver@2 将停止任何其他已注册的 EnpointExceptionResolver 来处理异常。

It took me embarrassingly long to figure out, but the answer is quite simple, in your servlet context you have to do this:我花了很长时间才弄清楚,但答案很简单,在您的 servlet 上下文中,您必须这样做:

<bean id="exceptionResolver" class="com.wdp.smm.ws.MyExceptionResolver">
    <property name="order" value="1"/>
</bean>

I've looked more closely at your question and I think that I know what's happening.我更仔细地研究了你的问题,我想我知道发生了什么。 You exception handler is not called because it is in the higher level in the soap processing.您的异常处理程序未被调用,因为它在 soap 处理中处于较高级别。 You see, WebServiceMessageReceiverHandlerAdapter tries to decode the incoming string to an XML before sending it to the marshaller to be processed.您会看到,WebServiceMessageReceiverHandlerAdapter 尝试将传入的字符串解码为 XML,然后再将其发送到编组器进行处理。 Since the XML is invalid the call fails.由于 XML 无效,调用失败。 And since WebServiceMessageReceiverHandlerAdapter does not support an exception handler, it just rethrows the exception "SaajSoapMessageException".由于 WebServiceMessageReceiverHandlerAdapter 不支持异常处理程序,它只是重新抛出异常“SaajSoapMessageException”。

Now what you can do is create a new class that extends WebServiceMessageReceiverHandlerAdapter, but that also wraps handleConnection() in a try/catch that uses your exception handler when a exception is throw.现在你可以做的是创建一个扩展 WebServiceMessageReceiverHandlerAdapter 的新类,但它也将 handleConnection() 包装在一个 try/catch 中,当抛出异常时使用你的异常处理程序。


By the way, when debugging this kind of problem my approach is to output both method name and line number in log4j.顺便说一句,在调试这类问题时我的做法是在log4j中同时输出方法名和行号。 As well as downloading the Spring sources.以及下载 Spring 源代码。

I think what @thierry-dimitri-roy explained is correct, however I had a lot of issues actually implementing it.我认为 @thierry-dimitri-roy 解释的是正确的,但是我在实际实施它时遇到了很多问题。 For example, just wrapping the handleconnection method isn't enough due to noendpointfoundexception not being thrown.例如,由于没有抛出 noendpointfoundexception,仅包装 handleconnection 方法是不够的。 As this is more general issue of handling exceptions nicely, I'm putting my code here to ease the pain for future generations.由于这是更好地处理异常的更普遍的问题,我将我的代码放在这里是为了减轻后代的痛苦。 This is tested with spring-ws 2.1.3 and JBoss AS7.这是用 spring-ws 2.1.3 和 JBoss AS7 测试的。

My message handler converts all problems to soap faults with response code 200.我的消息处理程序将所有问题转换为响应代码为 200 的 soap 故障。

package fi.eis.applications.spring.soap.server.transport.http;

import java.io.IOException;
import java.net.URISyntaxException;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.apache.commons.lang3.StringEscapeUtils;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.core.Ordered;
import org.springframework.web.servlet.HandlerAdapter;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.ws.FaultAwareWebServiceMessage;
import org.springframework.ws.InvalidXmlException;
import org.springframework.ws.NoEndpointFoundException;
import org.springframework.ws.WebServiceMessage;
import org.springframework.ws.WebServiceMessageFactory;
import org.springframework.ws.context.DefaultMessageContext;
import org.springframework.ws.context.MessageContext;
import org.springframework.ws.support.DefaultStrategiesHelper;
import org.springframework.ws.transport.EndpointAwareWebServiceConnection;
import org.springframework.ws.transport.FaultAwareWebServiceConnection;
import org.springframework.ws.transport.WebServiceConnection;
import org.springframework.ws.transport.WebServiceMessageReceiver;
import org.springframework.ws.transport.context.DefaultTransportContext;
import org.springframework.ws.transport.context.TransportContext;
import org.springframework.ws.transport.context.TransportContextHolder;
import org.springframework.ws.transport.http.HttpServletConnection;
import org.springframework.ws.transport.http.HttpTransportConstants;
import org.springframework.ws.transport.http.MessageDispatcherServlet;
import org.springframework.ws.transport.http.WebServiceMessageReceiverHandlerAdapter;
import org.springframework.ws.transport.support.TransportUtils;

/**
 * Adapter to map XML parsing and other low-level errors to SOAP faults instead of
 * server standard error pages. Also, this class will always use return code HTTP_OK
 * (status 200) to requests, even if there are errors.
 *  
 */
public class MyWebServiceMessageReceiverHandlerAdapter
    extends WebServiceMessageReceiverHandlerAdapter
    implements HandlerAdapter, Ordered, InitializingBean, ApplicationContextAware {

    private ApplicationContext context;
    @Override
    public void setApplicationContext(ApplicationContext applicationContext)
            throws BeansException {
        this.context = applicationContext;
    }

    @Override
    public void afterPropertiesSet() {
        DefaultStrategiesHelper defaultStrategiesHelper = new DefaultStrategiesHelper(MessageDispatcherServlet.class);
        WebServiceMessageFactory factory = defaultStrategiesHelper
                .getDefaultStrategy(WebServiceMessageFactory.class, context);
        setMessageFactory(factory);
    }

    public ModelAndView handle(HttpServletRequest httpServletRequest,
                               HttpServletResponse httpServletResponse,
                               Object handler) throws Exception {
        if (HttpTransportConstants.METHOD_POST.equals(httpServletRequest.getMethod())) {
            WebServiceConnection connection = new MyWebServiceConnection(httpServletRequest, httpServletResponse);
            try {
                overriddenHandleConnection(connection, (WebServiceMessageReceiver) handler);
            } catch (InvalidXmlException ex) {
                handleInvalidXmlException(httpServletRequest, httpServletResponse, handler, ex);
            } catch (Exception ex) {
                handleGeneralException(httpServletRequest, httpServletResponse, handler, ex);
            }
        }
        else {
            handleNonPostMethod(httpServletRequest, httpServletResponse, handler);
        }
        return null;
    }


    /**
     * Overridden version of handleConnection from WebServiceMessageReceiverObjectSupport to be able to handle
     * missing endpoint ourselves. That method is final, so we need to use another method here.
     * 
     * This has been reported as https://jira.springsource.org/browse/SWS-850
     * 
     * @param connection
     * @param receiver
     * @throws Exception
     */
    protected void overriddenHandleConnection(WebServiceConnection connection, WebServiceMessageReceiver receiver)
            throws Exception {
        logUri(connection);
        TransportContext previousTransportContext = TransportContextHolder.getTransportContext();
        TransportContextHolder.setTransportContext(new DefaultTransportContext(connection));

        try {
            WebServiceMessage request = connection.receive(getMessageFactory());
            MessageContext messageContext = new DefaultMessageContext(request, getMessageFactory());
            receiver.receive(messageContext);
            if (messageContext.hasResponse()) {
                WebServiceMessage response = messageContext.getResponse();
                if (response instanceof FaultAwareWebServiceMessage &&
                        connection instanceof FaultAwareWebServiceConnection) {
                    FaultAwareWebServiceMessage faultResponse = (FaultAwareWebServiceMessage) response;
                    FaultAwareWebServiceConnection faultConnection = (FaultAwareWebServiceConnection) connection;
                    faultConnection.setFault(faultResponse.hasFault());
                }
                connection.send(messageContext.getResponse());
            }
        }
        catch (NoEndpointFoundException ex) {
            if (connection instanceof EndpointAwareWebServiceConnection) {
                ((EndpointAwareWebServiceConnection) connection).endpointNotFound();
            }
            throw ex;
        }
        finally {
            TransportUtils.closeConnection(connection);
            TransportContextHolder.setTransportContext(previousTransportContext);
        }
    }

    private void logUri(WebServiceConnection connection) {
        if (logger.isDebugEnabled()) {
            try {
                logger.debug("Accepting incoming [" + connection + "] at [" + connection.getUri() + "]");
            }
            catch (URISyntaxException e) {
                // ignore
            }
        }
    }



    private void handleGeneralException(
            HttpServletRequest httpServletRequest,
            HttpServletResponse response, Object handler,
            Exception ex) throws IOException {
        writeErrorResponseWithMessage(response, ex.getClass().getName() + ": " + ex.getMessage());
    }

    /**
     * By default, sets SC_BAD_REQUEST as response in Spring, so overwritten to
     * provide HTTP_OK and reasonable SOAP fault response.
     */
    protected void handleInvalidXmlException(
            HttpServletRequest httpServletRequest,
            HttpServletResponse response, Object handler, InvalidXmlException ex)
            throws IOException {
        writeErrorResponseWithMessage(response, ex.getClass().getName() + ": " + ex.getMessage());
    }

    /**
     * By default, sets SC_METHOD_NOT_ALLOWED as response in Spring, so overwritten to
     * provide HTTP_OK and reasonable SOAP fault response.
     */
    protected void handleNonPostMethod(HttpServletRequest httpServletRequest,
                                       HttpServletResponse response,
                                       Object handler) throws Exception {
        writeErrorResponseWithMessage(response, "HTTP Method not allowed");
    }

    private void writeErrorResponseWithMessage(HttpServletResponse response, String message)
            throws IOException {
        String errorXml = String.format(
                 "<SOAP-ENV:Envelope xmlns:SOAP-ENV=\"http://schemas.xmlsoap.org/soap/envelope/\">"
                +"    <SOAP-ENV:Header/>"
                +"    <SOAP-ENV:Body>"
                +"        <SOAP-ENV:Fault>"
                +"            <faultcode>SOAP-ENV:Client</faultcode>"
                +"            <faultstring xml:lang=\"en\">%s</faultstring>"
                +"        </SOAP-ENV:Fault>"
                +"    </SOAP-ENV:Body>"
                +"</SOAP-ENV:Envelope>",
                StringEscapeUtils.escapeXml(message)
                );

        response.setStatus(HttpServletResponse.SC_OK);
        response.setContentType("text/xml");
        response.getWriter().write(errorXml);
        response.getWriter().flush();
    }
    @Override
    public int getOrder() {
        return 1;
    }

    /**
     * This class is needed as org.springframework.ws.transport.http.HttpServletConnection will throw an
     * exception if it is used outside Spring framework files. However, extending it and using the same
     * implementation seems to be fine.
     *
     */
    static class MyWebServiceConnection extends HttpServletConnection {
        protected MyWebServiceConnection(HttpServletRequest httpServletRequest,
                HttpServletResponse httpServletResponse) {
            super(httpServletRequest, httpServletResponse);
        }
    }    
}

This needs to be also configured correctly.这也需要正确配置。 This is the thing that is needed on spring context:这是 spring 上下文中需要的东西:

<!-- 'messageReceiverHandlerAdapter' is a magic name spring-ws
     org.springframework.ws.transport.http.MessageDispatcherServlet
     will bind to -->
<bean id="messageReceiverHandlerAdapter"
    class="fi.eis.applications.spring.soap.server.transport.http.MyWebServiceMessageReceiverHandlerAdapter">
</bean>

The solution I could come up with is to override the doService method in the MessageDispatcherServlet and capture the exception, then render a custom response.我想出的解决方案是覆盖 MessageDispatcherServlet 中的 doService 方法并捕获异常,然后呈现自定义响应。 This might not be the best solution for you, but it works for me, hope this helps!这可能不是您的最佳解决方案,但对我有用,希望对您有所帮助!

public class CustomMessageDispatcherServlet extends MessageDispatcherServlet {

protected void doService(HttpServletRequest request, HttpServletResponse response) throws Exception {
    try {
        super.doService(request, response);
    } catch (Exception e) {
        String error = e.getMessage();
        String errorXml = "<SOAP-ENV:Envelope xmlns:SOAP-ENV=\"http://schemas.xmlsoap.org/soap/envelope/\">" +
                                "<SOAP-ENV:Header/>" +
                                    "<SOAP-ENV:Body>" +
                                        "<SOAP-ENV:Fault>" +
                                            "<faultcode>SOAP-ENV:Client</faultcode>" +
                                            "<faultstring xml:lang=\"en\"></faultstring>" + error +
                                        "</SOAP-ENV:Fault>" +
                                    "</SOAP-ENV:Body>" +
                                "</SOAP-ENV:Envelope>";
        response.setStatus(HttpServletResponse.SC_OK);
        response.setContentType("text/xml");
        response.getWriter().write(errorXml);
        response.getWriter().flush();
    }
}

} }

You might want to catch only the exceptions you want to handle.您可能只想捕获要处理的异常。

Replace org.springframework.ws.transport.http.MessageDispatcherServlet with your CustomMessageDispatcherServlet in the web.xml file.将 org.springframework.ws.transport.http.MessageDispatcherServlet 替换为 web.xml 文件中的 CustomMessageDispatcherServlet。

<servlet>
    <servlet-name>web-services</servlet-name>
    <servlet-class>CustomMessageDispatcherServlet</servlet-class>
</servlet>

I remember running into this kind of problem when working with spring-security.我记得在使用 spring-security 时遇到过这种问题。 I also had issues with my exception resolver not being invoked under some condititions.我还遇到了在某些情况下未调用异常解析器的问题。

The problem then was that there is a filter chain that handles each request.那么问题是有一个过滤器链来处理每个请求。 The code that invokes the exception resolvers is a filter in this chain, but it is close to the end of the chain.调用异常解析器的代码是这个链中的一个过滤器,但它接近链的末尾。 Hence, if an exception ocurred somewhere within the filters before the exception resolver invoking filter, my resolver would never be invoked.因此,如果在异常解析器调用过滤器之前过滤器中某处发生异常,我的解析器将永远不会被调用。

I am guessing you suffer from something similar here, where envelope parse errors happen before the exception resolver invoking filter.我猜你在这里遇到了类似的事情,其中信封解析错误发生在异常解析器调用过滤器之前。 If that is the case, you will have to resort to some other way of handling those exceptions, for example a vanilla servlet filter.如果是这种情况,您将不得不求助于其他一些方式来处理这些异常,例如 vanilla servlet 过滤器。

In the latest Spring WS version you can use custom webservice message listener在最新的 Spring WS 版本中,您可以使用自定义 webservice 消息监听器

public class CustomWebServiceMessageListener extends WebServiceMessageListener {

private static final Logger LOG = LoggerFactory.getLogger(CustomWebServiceMessageListener.class);

@Override
public void onMessage(Message message, Session session) throws JMSException {
    try {
        this.handleMessage(message, session);
    } catch (JmsTransportException jmsTransportException) {
        LOG.error(jmsTransportException.getMessage(), jmsTransportException);
    } catch (Exception e) {
        JMSException jmsException = new JMSException(e.getMessage());
        jmsException.setLinkedException(e);
        LOG.error(e.getMessage(), e);
    }
  }
}

And inject it like this:并像这样注入它:

  <bean id="messageListenerContainer" class="org.springframework.jms.listener.DefaultMessageListenerContainer">
    <property name="concurrentConsumers" value="#{properties.concurrentListeners}"/>

    <property name="connectionFactory" ref="connectionFactory"/>
    <property name="destination" ref="queue"/>
    <property name="messageListener" ref="messageListener"/>
    <property name="sessionTransacted" value="true"/>

</bean>

<bean id="messageListener" class="com.yourproject.transport.CustomWebServiceMessageListener">
    <property name="messageFactory" ref="saajMessageFactory"/>
    <property name="messageReceiver" ref="messageDispatcher"/>
</bean>

Since the doService no longer throws an error for invalid XML and simply sets status to 400, I had to explore further on Chen's answer and implemented the following.由于 doService 不再为无效的 XML 抛出错误并且只是将状态设置为 400,我不得不进一步探索 Chen 的回答并实现了以下内容。

public class CustomWebServiceMessageReceiverHandlerAdapter extends WebServiceMessageReceiverHandlerAdapter {

  @Override
  protected void handleInvalidXmlException(HttpServletRequest httpServletRequest,
                                           HttpServletResponse httpServletResponse,
                                           Object handler,
                                           InvalidXmlException ex) throws IOException {
    String error = ex.getMessage();
    String errorXml = "<SOAP-ENV:Envelope xmlns:SOAP-ENV=\"http://schemas.xmlsoap.org/soap/envelope/\">" +
        "<SOAP-ENV:Header/>" +
        "<SOAP-ENV:Body>" +
        "<SOAP-ENV:Fault>" +
        "<faultcode>SOAP-ENV:Client</faultcode>" +
        "<faultstring xml:lang=\"en\"></faultstring>" + error +
        "</SOAP-ENV:Fault>" +
        "</SOAP-ENV:Body>" +
        "</SOAP-ENV:Envelope>";
    httpServletResponse.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
    httpServletResponse.setContentType("text/xml");
    httpServletResponse.getWriter().write(errorXml);
    httpServletResponse.getWriter().flush();
  }
}

On the bean configuration updated messageDispatcherServlet as below by setting messageReceiverHandlerAdapterBeanName to use the custom WebServiceMessageReceiverHandlerAdapter .在 bean 配置上,通过将messageReceiverHandlerAdapterBeanName设置为使用自定义WebServiceMessageReceiverHandlerAdapter来更新messageDispatcherServlet如下所示。

  @Bean
  public ServletRegistrationBean messageDispatcherServlet(ApplicationContext applicationContext) {
    MessageDispatcherServlet servlet = new MessageDispatcherServlet();
    servlet.setApplicationContext(applicationContext);
    servlet.setMessageReceiverHandlerAdapterBeanName("customMessageReceiverHandlerAdapter");
    return new ServletRegistrationBean(servlet, "/ws/*");
  }

  @Bean
  public CustomWebServiceMessageReceiverHandlerAdapter customMessageReceiverHandlerAdapter() {
    CustomWebServiceMessageReceiverHandlerAdapter customWebServiceMessageReceiverHandlerAdapter =
        new CustomWebServiceMessageReceiverHandlerAdapter();
    customWebServiceMessageReceiverHandlerAdapter.setMessageFactory(new DomPoxMessageFactory());
    return customWebServiceMessageReceiverHandlerAdapter;
  }

You have to "wire" (or "inject") the exception handler in your spring beans.您必须在 spring bean 中“连接”(或“注入”)异常处理程序。 I'm not sure which of your Spring bean actually needs the exception handler.我不确定您的哪个 Spring bean 实际上需要异常处理程序。

Personally, I use default-autowire="byName" , which cause my exceptionHandler to be wired in my Controller class automatically.就我个人而言,我使用default-autowire="byName" ,这会导致我的 exceptionHandler 自动连接到我的 Controller 类中。 Your approach actually use manual wiring.您的方法实际上使用手动接线。 So you need to find out which bean should actually use the exception handler.所以你需要找出哪个 bean 应该实际使用异常处理程序。 Have you tried (just on top of my head):你有没有试过(就在我头上):

<bean class="org.springframework.ws.server.endpoint.adapter.MarshallingMethodEndpointAdapter">
    <constructor-arg ref="marshaller"/>
    <property name="exceptionHandler" ref="exceptionHandler" />
</bean>

Or you could just add the autowired mechanism of Spring and let it wire the beans automatically.或者您可以只添加 Spring 的自动装配机制,让它自动装配 bean。 :) :)

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM