我使用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();
}
这可能吗?
答案 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将做两件事:
因此,添加自己的SOAP错误“catch all”错误处理的一种可能方法是使用基于此生命周期的拦截器。
您创建一个Interceptor实现(AbstractSoapInterceptor很适合)
您可以在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()
}
}
当“正常消息通过”时,此拦截器不会执行任何操作:
@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
或类似的东西进行自定义异常处理。