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

mem*_*und 2 java spring soap web-services cxf

我提供SOAP @WebMethod使用SpringCXF。而且我想捕获任何异常(已检查和未检查)并将其转换为custom @WebFault

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

<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);
    }
}
Run Code Online (Sandbox Code Playgroud)

因此,使用某种拦截器,我的方法将如下所示:

    @WebMethod
    public void test() {
        service.run();
    }
Run Code Online (Sandbox Code Playgroud)

那可能吗?

GPI*_*GPI 5

好吧,第一个可能会建议一种“低技术含量”的方法:为所有try/catch在一个invoke方法中处理web服务的基类提供一个基类。将此方法委托给抽象doInvoke方法,并使其成为所有JAXWS实现仅调用的策略invoke

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


但是,如果要构建自定义错误拦截,则可以在CXF级别上进行。

当与JAX-WS一起使用时,最简单形式的CXF可以被认为是JAX WS引擎周围的(复杂)拦截器链:拦截器链是CXF所有“实质”的去向。

CXF拦截器按链排列(“进”链,“故障”链,“出”和“出故障”链)。

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

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

在您的情况下,最重要的是ServiceInvokerInterceptor,属于INVOKE阶段的,负责调用@Webservice。处理完“输入”链中的所有拦截器后,CXF会将响应对象处理到“输出”链,以对输出进行序列化(或者,如果您有单向SOAP方法,则在此停止所有操作,这是特例)。

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

  1. 它将使用handleFault方法将链停在原处,并调用以相反顺序处理的所有拦截器。
  2. 然后它将控制权转发到故障拦截器链(“发生故障”,“发生故障”)。

因此,您添加自己的SOAP错误“全部捕获”错误处理的一种可能方法是使用基于此生命周期的拦截器。

您创建一个Interceptor实现(AbstractSoapInterceptor对此很有用),您可以在实现之前将其绑定到INVOKE阶段。 ServiceInvokerInterceptor

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()
    }
}
Run Code Online (Sandbox Code Playgroud)

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

@Override
public void handleMessage(SoapMessage message) throws Fault {
    // Do nothing
}
Run Code Online (Sandbox Code Playgroud)

但这是要处理的错误:

@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);...
}
Run Code Online (Sandbox Code Playgroud)

另一种实现方式是Fault通过custom 交换原始SoapFault内容Fault,如果这对您更有意义,则它也是的子类。

诚然,这比启动您自己的异常要困难得多,但是它允许您构建精确的,有意义的肥皂故障。但是请注意,优良作法是仅启动作为WSDL一部分存在的SOAP Fault,因此要与客户端“巧妙地”玩耍,不要在此处构建与WSDL不匹配的错误(在您的情况下为@Webfault定义)。

最后,您必须声明要将拦截器添加到链中。这样做有多种方法:基于每个bean:

<bean id="myIt" class="com.yourInterceptor" />
<jaxws:endpoint implementor="de.MyService" address="/MyService">
    <jaxws:inInterceptors>
      <ref bean="myIt"/>
    </jaxws:inInterceptor>
</jaxws:endpoint>
Run Code Online (Sandbox Code Playgroud)

或在巴士一级。

<cxf:bus>
    <cxf:inInterceptors>
        <ref bean="myIt"/>
    </cxf:inInterceptors>
</cxf:bus>
Run Code Online (Sandbox Code Playgroud)