Spring WS (DefaultWsdl11Definition) 无效的 HTTP 状态代码
Spring WS (DefaultWsdl11Definition) HTTP status code with void
我们有一个基于 Spring WS 的(工作中的)SOAP Web 服务,具有 DefaultWsdl11Definition.
基本上是这样的:
@Endpoint("name")
public class OurEndpoint {
@PayloadRoot(namespace = "somenamespace", localPart = "localpart")
public void onMessage(@RequestPayload SomePojo pojo) {
// do stuff
}
}
它已接入 Spring,并且正在正确处理我们所有的 SOAP 请求。唯一的问题是方法 return 是 202 Accepted。这 不是 来电者想要的,他宁愿让我们 return 204 No Content (或者如果那不可能空 200 OK).
我们的其他端点有一个有效的响应对象,并执行 return 200 OK。似乎 void 导致 202 而 204 可能更合适?
是否可以更改 Spring WS 中的响应代码?我们似乎无法找到执行此操作的正确方法。
我们尝试过但没有奏效的事情:
- 将 return 类型更改为:
- HttpStatus.NO_CONTENT
- org.w3c.dom.Element <- 未被接受
- 添加 @ResponseStatus <- 这是针对 MVC,而不是 WS
有什么想法吗?
创建委托类解决方案可能是最简单的方法,而不是我在评论中写的内容。
public class DelegatingMessageDispatcher extends MessageDispatcher {
private final WebServiceMessageReceiver delegate;
public DelegatingMessageDispatcher(WebServiceMessageReceiver delegate) {
this.delegate = delegate;
}
public void receive(MessageContext messageContext) throws Exception {
this.delegate.receive(messageContext);
if (!messageContext.hasResponse()) {
TransportContext tc = TransportContextHolder.getTransportContext();
if (tc != null && tc.getConnection() instanceof HttpServletConnection) {
((HttpServletConnection) tc.getConnection()).getHttpServletResponse().setStatus(200);
}
}
}
}
然后您需要配置一个名为 messageDispatcher
的 bean,它将包装默认值 SoapMessageDispatcher
。
@Bean
public MessageDispatcher messageDispatcher() {
return new DelegatingMessageDispatcher(soapMessageDispatcher());
}
@Bean
public MessageDispatcher soapMessageDispatcher() {
return new SoapMessageDispatcher();
}
类似的东西应该可以解决问题。现在,当创建响应时(在 void return 类型的情况下),您想要的状态将发送回客户端。
在寻找合适的解决方案时,我们遇到了一些难看的问题:
- 创建自定义 adapters/interceptors 有问题,因为当您没有响应时 handleResponse 方法不会被 Spring 调用(无效)
- 手动设置状态代码不起作用,因为 HttpServletConnection 保留一个布尔值 statusCodeSet,它不会被更新
但幸运的是,我们设法通过以下更改让它工作:
/**
* If a web service has no response, this handler returns: 204 No Content
*/
public class NoContentInterceptor extends EndpointInterceptorAdapter {
@Override
public void afterCompletion(MessageContext messageContext, Object o, Exception e) throws Exception {
if (!messageContext.hasResponse()) {
TransportContext tc = TransportContextHolder.getTransportContext();
if (tc != null && tc.getConnection() instanceof HttpServletConnection) {
HttpServletConnection connection = ((HttpServletConnection) tc.getConnection());
// First we force the 'statusCodeSet' boolean to true:
connection.setFaultCode(null);
// Next we can set our custom status code:
connection.getHttpServletResponse().setStatus(204);
}
}
}
}
接下来我们需要注册这个拦截器,这可以使用Spring的XML轻松完成:
<sws:interceptors>
<bean class="com.something.NoContentInterceptor"/>
</sws:interceptors>
非常感谢@m-deinum 为我们指明了正确的方向!
覆盖 afterCompletion 方法确实帮助我解决了完全相同的情况。对于那些使用基于代码的 Spring 配置的人,这里介绍了如何为特定端点添加拦截器。
用@Component注解自定义拦截器,然后像这样将自定义拦截器注册到WsConfigurerAdapter:
@EnableWs
@Configuration
public class EndpointConfig extends WsConfigurerAdapter {
/**
* Add our own interceptor for the specified WS endpoint.
* @param interceptors
*/
@Override
public void addInterceptors(List<EndpointInterceptor> interceptors) {
interceptors.add(new PayloadRootSmartSoapEndpointInterceptor(
new NoContentInterceptor(),
"NAMESPACE",
"LOCAL_PART"
));
}
}
NAMESPACE 和 LOCAL_PART 应该对应端点。
如果有人想在返回非空响应时设置自定义 HTTP 状态,这里是解决方案:
我们有一个基于 Spring WS 的(工作中的)SOAP Web 服务,具有 DefaultWsdl11Definition.
基本上是这样的:
@Endpoint("name")
public class OurEndpoint {
@PayloadRoot(namespace = "somenamespace", localPart = "localpart")
public void onMessage(@RequestPayload SomePojo pojo) {
// do stuff
}
}
它已接入 Spring,并且正在正确处理我们所有的 SOAP 请求。唯一的问题是方法 return 是 202 Accepted。这 不是 来电者想要的,他宁愿让我们 return 204 No Content (或者如果那不可能空 200 OK).
我们的其他端点有一个有效的响应对象,并执行 return 200 OK。似乎 void 导致 202 而 204 可能更合适?
是否可以更改 Spring WS 中的响应代码?我们似乎无法找到执行此操作的正确方法。
我们尝试过但没有奏效的事情:
- 将 return 类型更改为:
- HttpStatus.NO_CONTENT
- org.w3c.dom.Element <- 未被接受
- 添加 @ResponseStatus <- 这是针对 MVC,而不是 WS
有什么想法吗?
创建委托类解决方案可能是最简单的方法,而不是我在评论中写的内容。
public class DelegatingMessageDispatcher extends MessageDispatcher {
private final WebServiceMessageReceiver delegate;
public DelegatingMessageDispatcher(WebServiceMessageReceiver delegate) {
this.delegate = delegate;
}
public void receive(MessageContext messageContext) throws Exception {
this.delegate.receive(messageContext);
if (!messageContext.hasResponse()) {
TransportContext tc = TransportContextHolder.getTransportContext();
if (tc != null && tc.getConnection() instanceof HttpServletConnection) {
((HttpServletConnection) tc.getConnection()).getHttpServletResponse().setStatus(200);
}
}
}
}
然后您需要配置一个名为 messageDispatcher
的 bean,它将包装默认值 SoapMessageDispatcher
。
@Bean
public MessageDispatcher messageDispatcher() {
return new DelegatingMessageDispatcher(soapMessageDispatcher());
}
@Bean
public MessageDispatcher soapMessageDispatcher() {
return new SoapMessageDispatcher();
}
类似的东西应该可以解决问题。现在,当创建响应时(在 void return 类型的情况下),您想要的状态将发送回客户端。
在寻找合适的解决方案时,我们遇到了一些难看的问题:
- 创建自定义 adapters/interceptors 有问题,因为当您没有响应时 handleResponse 方法不会被 Spring 调用(无效)
- 手动设置状态代码不起作用,因为 HttpServletConnection 保留一个布尔值 statusCodeSet,它不会被更新
但幸运的是,我们设法通过以下更改让它工作:
/**
* If a web service has no response, this handler returns: 204 No Content
*/
public class NoContentInterceptor extends EndpointInterceptorAdapter {
@Override
public void afterCompletion(MessageContext messageContext, Object o, Exception e) throws Exception {
if (!messageContext.hasResponse()) {
TransportContext tc = TransportContextHolder.getTransportContext();
if (tc != null && tc.getConnection() instanceof HttpServletConnection) {
HttpServletConnection connection = ((HttpServletConnection) tc.getConnection());
// First we force the 'statusCodeSet' boolean to true:
connection.setFaultCode(null);
// Next we can set our custom status code:
connection.getHttpServletResponse().setStatus(204);
}
}
}
}
接下来我们需要注册这个拦截器,这可以使用Spring的XML轻松完成:
<sws:interceptors>
<bean class="com.something.NoContentInterceptor"/>
</sws:interceptors>
非常感谢@m-deinum 为我们指明了正确的方向!
覆盖 afterCompletion 方法确实帮助我解决了完全相同的情况。对于那些使用基于代码的 Spring 配置的人,这里介绍了如何为特定端点添加拦截器。
用@Component注解自定义拦截器,然后像这样将自定义拦截器注册到WsConfigurerAdapter:
@EnableWs
@Configuration
public class EndpointConfig extends WsConfigurerAdapter {
/**
* Add our own interceptor for the specified WS endpoint.
* @param interceptors
*/
@Override
public void addInterceptors(List<EndpointInterceptor> interceptors) {
interceptors.add(new PayloadRootSmartSoapEndpointInterceptor(
new NoContentInterceptor(),
"NAMESPACE",
"LOCAL_PART"
));
}
}
NAMESPACE 和 LOCAL_PART 应该对应端点。
如果有人想在返回非空响应时设置自定义 HTTP 状态,这里是解决方案: