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 导致 202204 可能更合适?

是否可以更改 Spring 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 状态,这里是解决方案: