Spring Web 服务 - 异常跳过 ExceptionResolver
我有一个 SOAP 服务,请求和响应在良好输入的情况下按预期工作,如果我
在请求正文中为 XML 元素指定错误输入:
...
<ns:myIntegerElement>asdf</ns:myIntegerElement>
...
我的异常解析器被调用,此解析器只是异常解析器的实现,因此它不会没有异常映射,只有一些 System.out 在抽象方法中
<bean id="exceptionResolver" class="com.mycompany.ws.MyExceptionResolver">
,但是,如果我发送一个看起来更像这样的请求:
...
<ns:myIntegSOMETHINGGOTTOTALLYMESSUP!!!ent>asdf</ns:myIntegerElement>
...
我的解析器根本没有执行
我设置 log4j 以获得根调试级别并查看此输出:
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 [线程:http-8080-2] 调试 [org.springframework.ws.transport.http.MessageDispatcherServlet] - 无法完成请求 org.springframework.ws.soap.saaj.SaajSoapMessageException: 无法访问信封:无法 从给定来源创建信封:; 嵌套异常是 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.transport.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) 在 javax.servlet.http.HttpServlet.service(HttpServlet.java:717) 在 org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.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.java: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) 在 org.springframework.ws.soap.saaj.support.SaajUtils.getSaajVersion(SaajUtils.java:159) ... 24 更多 引起原因:javax.xml.transform.TransformerException: org.xml.sax.SAXParseException: 元素类型“smm:smm-aid”必须是 由匹配的结束标记终止 “”。 在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”必须是 由匹配的结束标记终止 “”。 在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 更多
看来 spring 在这里缺少一个可能的异常,并且没有包装它,但是这样一个基本的错误条件没有被捕获对我来说似乎不太可能。谁能帮我找到这个问题的根源?
我还将包含 web.xml 和 servlet.xml:
web.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>
I have a SOAP service, the request and responses work as expected with good input, if I specify bad input for an XML element
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
<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:
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,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: ;
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.transport.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.java: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: The
element type "smm:smm-aid" must be
terminated by the matching end-tag
"".
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
"".
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
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. Can anyone help me to find the root of this issue?
I'll include web.xml and servlet.xml too:
web.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>
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(8)
MessageDispatcherServlet 默认创建自己的两个 EnpointExceptionResolver 实例,即
SoapFaultAnnotationExceptionResolver@1
和
SimpleSoapExceptionResolver@2
当它解决异常时,SimpleSoapExceptionResolver@2 将停止任何其他已注册的 EnpointExceptionResolver 来处理异常。
我花了很长时间才弄清楚,但答案很简单,
在你的 servlet 上下文中你必须这样做:
The MessageDispatcherServlet creates its own two instances of EnpointExceptionResolver by default, namely
SoapFaultAnnotationExceptionResolver@1
and
SimpleSoapExceptionResolver@2
And when it resolves an exception the SimpleSoapExceptionResolver@2 will stop any other registered EnpointExceptionResolver to handle an exception.
It took me embarrassingly long to figure out, but the answer is quite simple,
in your servlet context you have to do this:
我更仔细地研究了你的问题,我想我知道发生了什么。不会调用异常处理程序,因为它处于肥皂处理的更高级别。您会看到,WebServiceMessageReceiverHandlerAdapter 尝试将传入字符串解码为 XML,然后再将其发送到编组器进行处理。由于 XML 无效,调用失败。由于 WebServiceMessageReceiverHandlerAdapter 不支持异常处理程序,因此它只是重新抛出异常“SaajSoapMessageException”。
现在您可以做的是创建一个扩展 WebServiceMessageReceiverHandlerAdapter 的新类,但还将 handleConnection() 包装在 try/catch 中,在引发异常时使用您的异常处理程序。
顺便说一下,在调试此类问题时我的做法是在log4j中同时输出方法名和行号。以及下载 Spring 源代码。
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. You see, WebServiceMessageReceiverHandlerAdapter tries to decode the incoming string to an XML before sending it to the marshaller to be processed. Since the XML is invalid the call fails. And since WebServiceMessageReceiverHandlerAdapter does not support an exception handler, it just rethrows the exception "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.
By the way, when debugging this kind of problem my approach is to output both method name and line number in log4j. As well as downloading the Spring sources.
我认为@thierry-dimitri-roy 的解释是正确的,但是在实际实现它时我遇到了很多问题。例如,由于没有抛出 noendpointfoundException,仅包装 handleconnection 方法是不够的。由于这是更好地处理异常的更普遍的问题,因此我将我的代码放在这里以减轻后代的痛苦。这是使用 spring-ws 2.1.3 和 JBoss AS7 进行测试的。
我的消息处理程序将所有问题转换为响应代码为 200 的肥皂故障。
这也需要正确配置。这是 spring 上下文中需要的东西:
I think what @thierry-dimitri-roy explained is correct, however I had a lot of issues actually implementing it. For example, just wrapping the handleconnection method isn't enough due to noendpointfoundexception not being thrown. 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.
My message handler converts all problems to soap faults with response code 200.
This needs to be also configured correctly. This is the thing that is needed on spring context:
我能想到的解决方案是重写 MessageDispatcherServlet 中的 doService 方法并捕获异常,然后呈现自定义响应。这可能不是您的最佳解决方案,但它对我有用,希望这会有所帮助!
您
可能只想捕获您想要处理的异常。
将 org.springframework.ws.transport.http.MessageDispatcherServlet 替换为 web.xml 文件中的 CustomMessageDispatcherServlet。
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. This might not be the best solution for you, but it works for me, hope this helps!
}
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.
我记得在使用 spring-security 时遇到过这种问题。我还遇到了在某些情况下未调用异常解析器的问题。
那么问题是有一个过滤器链来处理每个请求。调用异常解析器的代码是该链中的过滤器,但它接近链的末尾。因此,如果在异常解析器调用过滤器之前过滤器内的某处发生异常,则我的解析器将永远不会被调用。
我猜您在这里遇到了类似的问题,其中信封解析错误发生在异常解析器调用过滤器之前。如果是这种情况,您将不得不采用其他方法来处理这些异常,例如普通 servlet 过滤器。
I remember running into this kind of problem when working with 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.
由于 doService 不再抛出无效 XML 的错误,并且只是将状态设置为 400,因此我必须进一步探索 Chen 的答案并实现以下内容。
在 bean 配置上,通过设置
messageReceiverHandlerAdapterBeanName
以使用自定义WebServiceMessageReceiverHandlerAdapter
,更新了messageDispatcherServlet
,如下所示。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.
On the bean configuration updated
messageDispatcherServlet
as below by settingmessageReceiverHandlerAdapterBeanName
to use the customWebServiceMessageReceiverHandlerAdapter
.在最新的 Spring WS 版本中,您可以使用自定义 Webservice 消息侦听器
并像这样注入它:
In the latest Spring WS version you can use custom webservice message listener
And inject it like this:
您必须在 spring bean 中“连接”(或“注入”)异常处理程序。我不确定您的哪个 Spring bean 实际上需要异常处理程序。
就我个人而言,我使用
default-autowire="byName"
,这会导致我的 exceptionHandler 自动连接到我的 Controller 类中。您的方法实际上使用手动接线。因此,您需要找出哪个 bean 应该实际使用异常处理程序。你尝试过吗(就在我的脑海中):或者你可以添加Spring的自动连接机制并让它自动连接bean。 :)
You have to "wire" (or "inject") the exception handler in your spring beans. I'm not sure which of your Spring bean actually needs the exception handler.
Personally, I use
default-autowire="byName"
, which cause my exceptionHandler to be wired in my Controller class automatically. Your approach actually use manual wiring. So you need to find out which bean should actually use the exception handler. Have you tried (just on top of my head):Or you could just add the autowired mechanism of Spring and let it wire the beans automatically. :)