答案 0 :(得分:6)
MessageDispatcherServlet默认创建自己的两个EnpointExceptionResolver实例,即 SoapFaultAnnotationExceptionResolver @ 1 和 SimpleSoapExceptionResolver @ 2
当它解决异常时,SimpleSoapExceptionResolver @ 2将停止任何其他已注册的EnpointExceptionResolver来处理异常。
我花了很长时间才弄明白,但答案很简单, 在你的servlet上下文中你必须这样做:
<bean id="exceptionResolver" class="com.wdp.smm.ws.MyExceptionResolver">
<property name="order" value="1"/>
</bean>
答案 1 :(得分:4)
我更仔细地看了你的问题,我想我知道发生了什么。您不会调用异常处理程序,因为它位于soap处理的更高级别。您看,WebServiceMessageReceiverHandlerAdapter尝试将传入的字符串解码为XML,然后将其发送到要处理的编组器。由于XML无效,因此调用失败。由于WebServiceMessageReceiverHandlerAdapter不支持异常处理程序,它只是重新抛出异常“SaajSoapMessageException”。
现在你可以做的是创建一个扩展WebServiceMessageReceiverHandlerAdapter的新类,但它也会在try / catch中包装handleConnection(),它会在抛出异常时使用你的异常处理程序。
顺便说一句,在调试这种问题时,我的方法是在log4j中输出方法名和行号。以及下载Spring源代码。
答案 2 :(得分:3)
我认为@ thierry-dimitri-roy所解释的是正确的,但我实际上有很多问题。例如,由于没有抛出noendpointfoundexception,仅仅包装handleconnection方法是不够的。由于这是处理异常的更普遍的问题,我将我的代码放在这里以减轻后代的痛苦。这是使用spring-ws 2.1.3和JBoss AS7测试的。
我的消息处理程序将所有问题转换为soap fault,响应代码为200。
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);
}
}
}
这也需要正确配置。这是春天语境中需要的东西:
<!-- '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>
答案 3 :(得分:1)
我记得在使用spring-security时遇到这种问题。我也有一些问题,我的异常解析器在某些条件下没有被调用。
问题是,有一个处理每个请求的过滤器链。调用异常解析器的代码是此链中的过滤器,但它接近链的末尾。因此,如果在异常解析器调用过滤器之前在过滤器内的某处发生异常,则永远不会调用我的解析器。
我猜你在这里遇到类似的东西,在异常解析器调用过滤器之前发生信封解析错误。如果是这种情况,您将不得不采用其他方式来处理这些异常,例如vanilla servlet过滤器。
答案 4 :(得分:0)
我能想到的解决方案是覆盖MessageDispatcherServlet中的doService方法并捕获异常,然后呈现自定义响应。这对你来说可能不是最好的解决方案,但它对我有用,希望这有帮助!
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();
}
}
}
您可能只希望捕获要处理的异常。
使用web.xml文件中的CustomMessageDispatcherServlet替换org.springframework.ws.transport.http.MessageDispatcherServlet。
<servlet>
<servlet-name>web-services</servlet-name>
<servlet-class>CustomMessageDispatcherServlet</servlet-class>
</servlet>
答案 5 :(得分:0)
在最新的Spring WS版本中,您可以使用自定义Web服务消息侦听器
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);
}
}
}
并像这样注入:
<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>
答案 6 :(得分:0)
由于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();
}
}
在bean配置上,通过将messageDispatcherServlet
设置为使用自定义messageReceiverHandlerAdapterBeanName
来更新WebServiceMessageReceiverHandlerAdapter
。
@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;
}
答案 7 :(得分:-1)
你必须在spring bean中“连接”(或“注入”)异常处理程序。我不确定你的哪个Spring bean实际上需要异常处理程序。
就个人而言,我使用default-autowire="byName"
,这会导致我的exceptionHandler自动连接到我的Controller类中。您的方法实际上使用手动布线。所以你需要找出实际使用异常处理程序的bean。你有没有尝试过(只是在我的头顶):
<bean class="org.springframework.ws.server.endpoint.adapter.MarshallingMethodEndpointAdapter">
<constructor-arg ref="marshaller"/>
<property name="exceptionHandler" ref="exceptionHandler" />
</bean>
或者你可以添加Spring的自动装配机制,让它自动连接bean。 :)