加入soap上下文

soap上下文配置

和spring framework不同,spring web service不支持代码配置,小例子中,我们采用了混合配制的方式,通过SoapServletContextConfiguration扫描@org.springframework.ws.server.endpoint.annotation.Endpoint标记,同时导入ws的soapServletContext.xml配置文件。

//... ...
import org.springframework.ws.server.endpoint.annotation.Endpoint;

@Configuration
@ComponentScan(
        basePackages = "cn.wei.flowingflying.customer_support.site",
        useDefaultFilters = false,
        includeFilters = @ComponentScan.Filter(Endpoint.class)
)
@ImportResource("classpath:cn/wei/flowingflying/customer_support/config/soapServletContext.xml")
public class SoapServletContextConfiguration {
    /* The explicit messageFactory bean (the name is important) overrides the default message factory so
     * that the supported SOAP version is 1.2 instead of the default 1.1. */
    @Bean
    public WebServiceMessageFactory messageFactory(){
        SaajSoapMessageFactory factory = new SaajSoapMessageFactory();
        factory.setSoapVersion(SoapVersion.SOAP_12);
        return factory;
    }
}

我们在src/main/resources下,建立cn/wei/flowingflying/customer_support/config/soapServletContext.xml文件,用于告诉spring web service使用哪个XSD schema文件来生成WSDL。

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:sws="http://www.springframework.org/schema/web-services"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
            http://www.springframework.org/schema/beans/spring-beans-4.3.xsd
            http://www.springframework.org/schema/web-services
            http://www.springframework.org/schema/web-services/web-services-2.0.xsd">

    <sws:annotation-driven marshaller="jaxb2Marshaller"
                           unmarshaller="jaxb2Marshaller" />
    <sws:dynamic-wsdl id="support" portTypeName="Support"
                      locationUri="/services/Soap/" createSoap11Binding="false"
                      createSoap12Binding="true"
                      targetNamespace="http://www.example.org/support">
        <sws:xsd location="/WEB-INF/xsd/soap/support.xsd" />
    </sws:dynamic-wsdl>
</beans>

这里targetNamespace是定义自动生成wdsl的targetNamespace,可以和我们在xsd schema中定义的不一样,例如http://www.example.org/support123,那么生成的wsdl为:

<wsdl:definitions targetNamespace="http://www.example.org/support123">
  <wsdl:types>
    <schema attributeFormDefault="unqualified" elementFormDefault="qualified" targetNamespace="http://www.example.org/support"> 
       <element name="ticketsRequest" type="support:ticketsRequestType"/>
       <complexType name="ticketsRequestType"/>
......

虽然这是允许的,但是就代码的可读性和可维护性而言,我们应当将两者设置为相同。


root上下文中不扫描@Endpoint

@ComponentScan(
    basePackages = "cn.wei.chapter16.site",
    excludeFilters = @ComponentScan.Filter({Controller.class, ControllerAdvice.class, Endpoint.class})
)
public class RootContextConfiguration

启动时加载soap dispatcher servlet

AnnotationConfigWebApplicationContext soapContext = new AnnotationConfigWebApplicationContext();
soapContext.register(SoapServletContextConfiguration.class);
//Spring Web Service提供了一个Dispather MessageDispatcherServlet来处理soap请求
MessageDispatcherServlet soapServlet = new MessageDispatcherServlet(soapContext);
soapServlet.setTransformWsdlLocations(true);
dispatcher = container.addServlet("springSoapDispatcher", soapServlet);
dispatcher.setLoadOnStartup(3);
dispatcher.addMapping("/services/Soap/","/services/Soap/support.wsdl");

我们注意到在启动时,为dispatcher设置了mapping "/services/Soap/",当然我们也可以写成"/services/Soap/*",这涉及到soap消息如何定位。

  • 在配置文件soapServletContext.xml文件中,有locationUri="/services/Soap/",但经过我们测试,并不实际起作用。但如果不提供,启动时会报错,虽然不起作用,我们应保持配置文件中的设置和启动代码的一致。
  • ws区分soap消息,依靠的消息中的namespace,也即依赖于soap消息里面的内容,而不是底层承载的http的url。因此对于soap接口,指定一个url即可。
  • 如果使用了"/services/Soap/*",那么对于某个SOAP消息,访问/services/Soap/可以得到正确的结果,访问/services/Soap/123也可以,这可能不是我们需要的。
  • 处理提供soap接口外,还提供查看wsdl的接口,例如http://localhost:8080/customer-support/services/Soap/support.wsdl,我们必须允许这个链接是soap上下文的。如果有很多的xsd schema文件,且可能在开发过程中进行调整,这是使用/services/Soap/*可能会更为方便。

实现SOAP Endpoint

@Endpoint //ws的标记
public class TicketSoapEndpoint {
    private static final Logger logger = LogManager.getLogger();
    private static final String NAMESPACE = "http://www.example.org/support"; 
    @Inject private TicketService ticketService;

    // Spring Web Service提供了@PayloadRoot、@SoapAction和@Action来过滤请求  
    //@PayloadRoot中namespace配置很重要,这是soap消息的匹配。在soapServletContext.xml的配置文件中有targetNamespace,在wsdl文件support.xsd中有targetNamespace。而这里的namespace是检查请求消息中的namespace,并进行过滤,虽然可以和配置文件以及wsdl文件中设置得不一样,不影响其找到相应的wsdl接口定义,但是我们仍应三处保持一致。
    // @ResponsePayload或者RequestPayload表示soapbody中的净荷。   
    @PayloadRoot(namespace = NAMESPACE, localPart = "ticketsRequest")
    @ResponsePayload
    public TicketWebServiceList read(){
        TicketWebServiceList list = new TicketWebServiceList();
        list.setValue(this.ticketService.getAllTickets());
        return list;        
    }

    /* 这里prefix设置为s,只是将s作为prefix的标记,用于在@XPathParam进行标识,并发要求真的为s。
    下面合法的匹配消息:
     <soap:Envelope xmlns:soap="http://www.w3.org/2003/05/soap-envelope">
     <soap:Header/>
     <soap:Body>
       <support:ticketRequest xmlns:support="http://www.example.org/support">
         <id>1</id>
       </support:ticketRequest>
     </soap:Body>
     </soap:Envelope>
    soap body也可以写成
     <ticketRequest xmlns="http://www.example.org/support">
       <id>1</id>
     </ticketRequest>
    或者
     <a:ticketRequest xmlns:a="http://www.example.org/support">
       <id>1</id>
     </a:ticketRequest> */ 
    @PayloadRoot(namespace = NAMESPACE, localPart = "ticketRequest")
    @Namespace(uri = NAMESPACE, prefix = "s") 
    @ResponsePayload
    public Ticket read(@XPathParam("/s:ticketRequest/id") long id) {
        Ticket ticket = this.ticketService.getTicket(id);
        logger.info("ticket create time : {}", ticket.getDateCreated());
        return ticket;
    }

    /* 将soap消息体整个作为对象传递进来 */ 
    @PayloadRoot(namespace = NAMESPACE, localPart = "createTicket")
    @ResponsePayload
    public Ticket create(@RequestPayload CreateTicket form) {
      ......
    }
 ......   
}

我们看看消息的交互,需要注意,请求消息中应该设置Content-type和Accept为application/soap+xml,否则会返回500的错误,ws认为不是合法的soap消息。

Java for Web学习笔记(八五):SOAP(2)小例子_xml


相关链接: 我的Professional Java for Web Applications相关文章