【问题标题】:Convert SOAP to a virtual Restful service?将 SOAP 转换为虚拟的 Restful 服务?
【发布时间】:2011-06-19 22:39:07
【问题描述】:

有没有办法将我的 SOAP Web 服务(spring-ws,java)虚拟地用作基于 XML 的 RESTful 服务?

我不想在 java 中从头开始将我的整个 SOAP Web 服务重写为 RESTful,但我需要使用 REST 通过 iphone 访问它,他们已经有了简单的原生支持。

XMLGateway、代理..?或者一些额外的java代码?既然我的 SOAP 请求和响应只是一个 XML 文件,为什么我不能修改它以供 REST 服务使用?

或者在我的应用程序中不改变任何逻辑和 xml 解析是否很容易添加 jax-rs 注释并创建一个休息请求 xml?

我的配置spring文件是这样的:

<bean id="webServicePluginDescriptor"
    class="com.mysite.ws.configuration.MyWebservicePluginDescriptor" />

<bean id="payloadMapping"
    class="org.springframework.ws.server.endpoint.mapping.PayloadRootQNameEndpointMapping">
    <property name="defaultEndpoint" ref="inferenceEndPoint" />
    <property name="interceptors">
        <list>
            <ref local="validatingInterceptor" />
            <ref local="payLoadInterceptor" />
        </list>
    </property>
</bean>

<bean id="payLoadInterceptor"
    class="org.springframework.ws.server.endpoint.interceptor.PayloadLoggingInterceptor" />

<bean id="validatingInterceptor"
    class="org.springframework.ws.soap.server.endpoint.interceptor.PayloadValidatingInterceptor">
    <property name="schema"
        value="classpath:/wsdl/Request.xsd" />
    <property name="validateRequest" value="true" />
    <property name="validateResponse" value="false" />
</bean>

<bean id="PropertyResource" class="com.mysite.ws.im.PropertyResource">
    <property name="resource"
        value="/WEB-INF/client-specific/InferenceMachine.properties" />
</bean>

<bean id="inferenceEndPoint" class="com.mysite.ws.web.InferenceEndPoint">
    <property name="messageWebService" ref="messageWebService" />
</bean>
<bean id="messageWebService" class="com.mysite.ws.service.MessageWebService"
    scope="request">
    <aop:scoped-proxy />
    <property name="inferenceService" ref="inferenceService" />
</bean>

<bean id="Request" class="org.springframework.xml.xsd.SimpleXsdSchema">
    <property name="xsd" value="classpath:/wsdl/Request.xsd" />
</bean>

<bean id="Response" class="org.springframework.xml.xsd.SimpleXsdSchema">
    <property name="xsd" value="classpath:/wsdl/Response.xsd" />
</bean>

<bean id="Error" class="org.springframework.xml.xsd.SimpleXsdSchema">
    <property name="xsd" value="classpath:/wsdl/Error.xsd" />
</bean>

<bean id="mwsid"
    class="org.springframework.ws.wsdl.wsdl11.SimpleWsdl11Definition">
    <constructor-arg value="classpath:/wsdl/mtchwsdl.wsdl" />
</bean>

<bean id="inferenceService" class="com.mysite.ws.im.InferenceService"
    scope="request">
    <aop:scoped-proxy />
    <property name="webServiceConfiguration" ref="wsPlayerConfiguration" />

    <property name="properties">
        <bean class="com.mysite.ws.im.PropertyResource">
            <property name="resource"
                value="/WEB-INF/client-specific/InferenceMachine.properties" />
        </bean>
    </property>
</bean>

<!-- ~~~~~~~ Application beans ~~~~~~~ -->
<bean id="wsPlayerConfiguration"
    class="com.mysite.ws.configuration.WebServiceConfiguration"
    scope="request">
    <aop:scoped-proxy />
    <property name="playerConfiguration" ref="playerConfiguration"></property>
    <property name="configurationSetup" ref="configurationSetup"></property>
</bean>

这是我的端点类:

/**
 * The EndPoint of the Web Service Application. This class gets the raw
 * SOAP-body message from the Spring Payload Dispatcher and sends the message to
 * the @see MessageService class. After it has gotten the response XML message
 * it returns this back to the Spring Payload Dispatcher.
 */
public class InferenceEndPoint extends AbstractJDomPayloadEndpoint {

    private MessageWebService messageWebService;
    public InferenceEndPoint() {
    }

    @Override
    protected Element invokeInternal(Element inferenceRequest) throws Exception {
        Element ret = messageWebService.handleRequest(inferenceRequest);
        return ret;
    }

    /**
     * @param messageWebService
     */
    public void setMessageWebService(MessageWebService messageWebService) {
        this.messageWebService = messageWebService;
    }
}

有什么想法吗?

【问题讨论】:

    标签: java web-services rest soap spring-ws


    【解决方案1】:

    Spring-WS 只是为您的 bean 添加一些注释,然后您就可以让 Spring bean 完成大部分繁重的工作。大概你有一些用@Endpoint、@PayloadRoot 等注释的类。您应该能够通过以下三种方式之一重复使用所有这些。

    如果您的 Spring-WS 端点类是适配器模式样式(例如,您的 Endpoint 类被注入了真正工作的 POJO 服务),那么您可以执行类似的适配器样式 Spring MVC 控制器(其中 REST 存在于 Spring 3.0)。

    如果您将注释直接放在业务逻辑类上,那么理论上,您应该可以再添加一些注释(可能看起来有点忙)。

    如果您的 Spring-WS bean 是 POX(而不是 SOAP),那么您可能能够摆脱一些花哨的 URL 映射,从而为它们提供更具 RESTful 外观的 URL

    要迁移到 Spring 3 以获得 REST 支持,请添加适当的 @RequestMapping 和另一个注释以将它们公开为 REST 服务以匹配特定的 URL。在添加时,您不妨删除旧的@PayloadRoots 和@Endpoint,但这可能没什么大不了的。当然,如果您留下旧的 Spring-WS 注释,您仍然需要在类路径中使用 Spring-WS jar,但只要您没有在 Spring 文件中使用 Spring-WS servlet 或任何其他 bean - 您应该没问题(理论上...)。

    最大的问题是:

    • 不要忘记从 Spring 文件中删除 Spring-WS bean
    • 记得将 Spring MVC bean 添加到您的 Spring 文件中,最重要的是不同的 Dispatcher servlet
    • Spring 中的 REST 安全性将由 Spring Security 提供,与 Spring-WS 中的 SOAP 拦截器不同,因此这将是一次彻底的检查。好消息是 Spring Security 实际上很容易使用

    【讨论】:

    • 谢谢,我在上面添加了config和ednpoint类,你能看看吗?有趣的是,我的任何业务类中都没有 @PayloadRoots 和 @Endpoint 或任何类型的注释,我也不知道我的端点类有什么模式? tnx
    • 你能告诉我这次谈话我需要触摸的地方吗?
    • 基于您的端点,您可能可以创建一个新的 Spring 控制器,在其上添加 @RequestMapping 注释,就像您的端点一样,您的控制器将被注入您的消息服务 bean
    【解决方案2】:

    查看您的代码,很难判断最佳方法是什么。 REST 和 SOAP 是想象基于 Web 的服务接口如何工作的完全不同的方式:SOAP 是关于方法调用的,而 REST 是关于资源、表示和链接的。要进行转换,您必须从底层抽象 API 开始。

    如果您的基本 API 是“我给你一个文档,你给我一个响应文档”之一,并且除此之外没有任何其他内容,那么它是一个非常面向 SOAP 的模型。您可以通过发布文档并获得响应在 REST 中对其进行建模,但这一点也不优雅。如果您可以将您的界面视为“这是一个整体资源,具有我可以设置的属性,以及我可以执行的某些操作”,那么它更容易映射到 REST(整体资源表示为一个充满链接的文档到单个属性资源和操作,并且单个属性可以是 GET 和 PUT - 并且可能是 DELETEd - 根据需要)。你有哪种风格……嗯,它看起来很像你有第一种,但我只是猜测,因为要确定它是真实的,需要查看比你展示的更多的代码。

    【讨论】:

    • 我可以通过 Iphone 访问我的 SOAP 服务,使用简单的 http 发布我的肥皂信封。所以我需要做很多工作来解析那个xml并从中创建类,所以如果我通过发送接收文档以(非优雅的方式)使用REST,那么实际上你的意思是我会做同样的事情作为肥皂?在客户端中完成的工作量很重要。如果我每次使用 SOAP 或 REST 都必须解析 xml 并从中创建对象,那么为什么我需要制作一个 RESTful 版本?我说的对吗?
    【解决方案3】:

    没有什么能阻止您发送填充 XML 的 POST 以返回填充 XML 的结果。

    最简单的做法是以某种方式捕获来回的 SOAP 请求,然后简单地将请求转换为模板,其中空白是您的参数,然后在生成的 XML 上使用 XPath 来提取结果。

    唯一的问题是您可能在 POST 中需要 SOAPAction 标头,但可能不需要。

    这真的没什么大不了的。如果你有几十种方法,那就更痛苦了。此外,如果您正在使用 SOAP 的任何加密部分,那就更痛苦了。但如果只是少数,最后它只是 XML,而且大部分 XML 都是样板文件,所以这样看,很简单。

    附录:

    如果你有后端逻辑,你想在前端使用更友好的 HTTP 服务,那么 JAX-RS 可以很容易地做到这一点,但它需要在服务器端进行编码。

    如果您希望使用现有的 SOAP Web 服务,那么请忘记等式中的整个 SOAP 部分,只需将其视为使用 XML 有效负载的 HTTP Web 服务。它仍然是 SOAP,但您没有在客户端使用任何 SOAP 工具。您只是在客户端请求上组装 XML 有效负载(从模板最简单,恕我直言),并因此使用 XML 有效负载,并通过 HTTP 进行交易。

    根据您打算调用的现有 Web 服务上有多少不同的方法,您可以了解所涉及的工作范围。这是一项简单的工作(一旦您可以轻松查看有效负载),但它仍然有效。如果您只有几个方法,特别是如果接口稳定且不发生变化,那么只使用原始 XML 比学习和对抗一些新的不熟悉的框架要容易得多。

    【讨论】:

    • Tnx,你的想法是在客户端还是在webservice上实现?几十种方法是什么意思?因此,例如,一旦我在准备通过 SOAP 发送的 web 服务中创建了我的 response.xml,我会接受它,再次解析它并从中创建我的其余参数?您如何看待 jax-rs 注释
    • 感谢您提供大量信息,我想我会使用 JAX-rs 解决方案,具体步骤是什么?
    【解决方案4】:

    这就是我使用 Spring Boot + Spring Integration 解决该问题的方法。

    • 拥有 SOAP WS 的 WSDL,我使用 maven-jaxb2-plugin 在构建时生成我的 Java POJO。
    • 或者,您可以创建转换以使这些类和属性适应 REST 客户端的预期。
    • 使用 Spring Integration,我将每个 REST 端点映射到一个 SOAP 网关,如下所示:
    @Bean
    public IntegrationFlow myFlow(GenericTransformer reqTransformer, GenericTransformer resTransformer) {
        return IntegrationFlows
            .from(this.getRestGateway(POST, "/api/entity", MyRestResponse.class))
            .transform(reqTransformer)
            .handle(this.getSoapGateway("gwBean"))
            .enrichHeaders(h -> h.header(HttpHeaders.STATUS_CODE, HttpStatus.OK))
            .transform(resTransformer)
            .logAndReply();
    }
    
    private HttpRequestHandlingMessagingGateway getRestGateway(HttpMethod method, String path, Class payloadType) {
        HttpRequestHandlingMessagingGateway httpGateway = new HttpRequestHandlingMessagingGateway();
        RequestMapping requestMapping = new RequestMapping();
        requestMapping.setMethods(method);
        requestMapping.setPathPatterns(path);
        httpGateway.setRequestMapping(requestMapping);
        httpGateway.setReplyTimeout(timeout);
        httpGateway.setRequestPayloadTypeClass(payloadType);
        httpGateway.setMessageConverters(asList(jsonConverter));
        return httpGateway;
    }
    
    private MarshallingWebServiceOutboundGateway getSoapGateway(String nameBean) {
        Jaxb2Marshaller marshaller = new Jaxb2Marshaller();
        marshaller.setContextPath(generatedClassesPackage);
        SaajSoapMessageFactory messageFactory = new SaajSoapMessageFactory();
        messageFactory.setSoapVersion(SoapVersion.SOAP_12);
        messageFactory.afterPropertiesSet();
        MarshallingWebServiceOutboundGateway webServiceGateway = new MarshallingWebServiceOutboundGateway(soapUri, marshaller);
        webServiceGateway.setMessageFactory(messageFactory);
        webServiceGateway.setBeanName(nombreBean);
        return webServiceGateway;
    }
    

    【讨论】:

    • 我不知道 Spring Integration。但这会改变请求还是我们需要做任何其他自定义实现?
    猜你喜欢
    • 2011-11-20
    • 1970-01-01
    • 2011-08-21
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多