如何捕获SOAP Web服务方法中的任何异常?

时间:2014-08-07 10:53:41

标签: java spring web-services soap cxf

我使用SOAP@WebMethod提供Spring CXF。我想捕获任何异常(已选中和未选中)并将其转换为自定义@WebFault

我可以以某种方式将错误处理程序/拦截器分配给我的@WebSerivce类,这样我就不必为每个webserivce方法提供额外的try-catch块吗?

<jaxws:endpoint implementor="de.MyService" address="/MyService" />

@Component
@WebService
public class MyService {
    @WebMethod
    public void test() throws MyException {
        try {
            service.run();
        } catch (Exception e) {
            throw new MyException("test");
        }
    }
}


@WebFault
public class MyException extends Exception {
    public MyException(String message) {
        super(message);
    }
}

因此,对于某种拦截器,我的方法看起来像这样:

    @WebMethod
    public void test() {
        service.run();
    }

这可能吗?

2 个答案:

答案 0 :(得分:5)

好吧,第一个可以建议采用“低技术”方法:为所有处理try/catch方法invoke的网络服务提供基类。让此方法委托给一个抽象的doInvoke方法,并使其成为所有JAXWS实现的策略,只调用invoke

@ kan的涉及AOP的解决方案也确定是一种可能的解决方案。


但是,如果要构建自定义错误拦截,可以在CXF级别执行此操作。

当与JAX-WS一起使用时,最简单形式的CXF可以被认为是围绕JAX WS引擎的(复杂)拦截器链:拦截器链是CXF的所有“肉”所在。< / p>

CXF拦截器被安排成链(“在”链中,“在故障”链中,“输出”和“输出故障”链)。

每个链都有各种“阶段”,例如:RECEIVE, (PRE/USER/POST)_STREAM, READ, (PRE/USER/POST)PROTOCOL, UNMARSHAL, (PRE/USER/POST)LOGICAL, PRE_INVOKE, INVOKE, POST_INVOKE是进入链的默认阶段。

拦截器“按顺序”执行(阶段与优先级相关联,拦截器实现声明它们属于哪个阶段。在一个阶段中,每个拦截器都可以选择放置在某个其他拦截器类之前或之后)。

在您的情况下最重要的是,属于ServiceInvokerInterceptor阶段的INVOKE负责调用@Webservice。当处理“in”链中的所有拦截器时,CXF将响应对象处理到“out”链以序列化输出(或者如果你有单向SOAP方法,则停止所有内容,这是一种特殊情况)。 p>

如果标准链中的任何地方发生异常,CXF将做两件事:

  1. 它会停止它所在的链并使用handleFault方法调用以相反顺序处理的所有拦截器
  2. 然后它将控制转发到故障拦截器链(“故障”,“故障”)。
  3. 因此,添加自己的SOAP错误“catch all”错误处理的一种可能方法是使用基于此生命周期的拦截器。

    您创建一个Interceptor实现(AbstractSoapInterceptor很适合) 您可以在ServiceInvokerInterceptor

    之前将其绑定到INVOKE阶段
    public class YourInterceptor extends AbstractSoapInterceptor {
        public YourInterceptor() {
            super(Phase.INVOKE);
            addBefore(Arrays.asList(ServiceInvokerInterceptor.class.getName()));
            // This means handleMessage will be called juste before your @WebMethod
            // If it fails, you will be the first to be noticed through #handleFault()
        }
    }
    

    当“正常消息通过”时,此拦截器不会执行任何操作:

    @Override
    public void handleMessage(SoapMessage message) throws Fault {
        // Do nothing
    }
    

    但它是为了处理错误:

    @Override
    public void handleFault(SoapMessage message) {
        // Every exception will be wrapped into a Fault object by CXF
        Fault f = (Fault) message.getContent(Exception.class);
        // You should inspect its g.getCause() to maybe identify what went wrong
        // A CXF Fault also much ressembles a SOAPFault element
        f.setMessage("Your SOAP Fault message");
        // You can access the DOM detail of the fault
        Element detail = f.getOrCreateDetail();
        Element newDetailEntry = detail.getOwnerDocument().createElementNS("detailNs", "detailName");
        newDetailEntry.setTextContent("Content for your soap fault detail");
        detail.appendChild(newDetailEntry);
        // And so on. f.setFaultCode(qName);...
    }
    

    另一种实现方式是将原始Fault替换为自定义SoapFault,如果它对您更有意义,它也是Fault的子类。

    这引起你自己的例外更加困难,但它允许你建立精确,有意义的肥皂故障。但请注意,最好只启动作为WSDL一部分存在的SOAP Fault,以便与客户“很好地”玩,不要在这里构建与WSDL不匹配的错误(在您的情况下,{{1定义)。

    您最终必须声明要将拦截器添加到链中。有多种方法可以做到这一点:基于每个bean:

    @Webfault

    或者在公交车级别。

    <bean id="myIt" class="com.yourInterceptor" />
    <jaxws:endpoint implementor="de.MyService" address="/MyService">
        <jaxws:inInterceptors>
          <ref bean="myIt"/>
        </jaxws:inInterceptor>
    </jaxws:endpoint>
    

答案 1 :(得分:1)

我认为它不是特定于SOAP的,您只需要一个java方法将一个异常转换为另一个异常。所以这意味着应该将bean方法包装到异常转换器中。因此,只需使用AOP环绕bean并通过@AfterThrowing或类似的东西进行自定义异常处理。