关于 java:WebService 无法处理没有命名空间前缀的 SOAP Body 请求

WebService can't handle request with SOAP Body not having a namespace prefix

当客户端调用 Web 服务时,我的 Web 服务无法处理我的客户端的请求,而没有在 SOAP 正文中传递前缀,如下所示:

1
2
3
4
5
6
7
8
9
10
11
<soap:Body>
 <GetPatientResultsRequest xmlns="http://urlA">
  <PatientIdentification>
      <PersonCivilRegistrationIdentifier xmlns="http://UrlB"/>
  </PatientIdentification>
  <Period>
    <From>2012-05-26</From>
     <To>2012-06-26</To>
   </Period>
 </GetPatientResultsRequest>
</soap:Body>

错误是GetPatientResultsRequest等对应的Java对象为null。

似乎当 Body 中没有前缀时,反序列化没有正确进行。只有当 SOAP 主体具有像

这样的前缀时,我的 Web 服务才能响应

1
2
3
4
5
6
7
8
9
10
11
<soap:Body>
 <m:GetPatientResultsRequest xmlns:m="http://urlA">
  <PatientIdentification>
      <PersonCivilRegistrationIdentifier xmlns="http://UrlB"/>
  </PatientIdentification>
  <Period>
    <From>2012-05-26</From>
     <To>2012-06-26</To>
   </Period>
 </m:GetPatientResultsRequest>
</soap:Body>

谁能告诉我该怎么做才能让我的 Web 服务接受各种类型的 SOAP 请求(即在 Body 中带和不带前缀)?

我正在使用 JAX-WS (SOAP 1.1)


Web 服务定义了一个约定,您必须遵循该约定才能调用它。您发布的示例中只有一条消息与该合同匹配,因此一个有效,另一个无效。

在您的第一条消息中,您定义了一个默认命名空间(因为package器中的 xmlns 属性)并且所有未取消声明它且没有前缀的元素都在同一命名空间中,因为它们继承自他们的父母。

在您的第二条消息中,您有一个明确的前缀声明,并且只有package器在该命名空间中,其他元素不在命名空间中,并且不会从父级继承默认元素(因为缺少 xmlns 属性)。

正如我开头所说,Web 服务定义了一个契约。修改客户端以发送正确的消息而不是更改服务以接受来自客户端的错误消息更有意义。

要控制元素的命名空间,您需要在 Web 服务和客户端的 JAX-WS 注释上使用 targetNamespace 值。

这是一个示例,用于查看更改目标命名空间时代码和消息格式的差异。我将为此使用基本的 WSDL:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
<?xml version="1.0" encoding="utf-8"?>
<wsdl:definitions
xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/"
xmlns:tns="http://tempuri.org"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/"
targetNamespace="http://tempuri.org"
name="CalculatorWS">
  <wsdl:types>
    <xs:schema targetNamespace="http://tempuri.org">
      <xs:element name="add" type="tns:add" />
      <xs:element name="addInput" type="tns:addInput" />
      <xs:element name="addResponse" type="tns:addResponse" />
      <xs:element name="addOutput" type="tns:addOutput" />
      <xs:complexType name="add">
        <xs:sequence>
          <xs:element name="addInput" type="tns:addInput" />
        </xs:sequence>
      </xs:complexType>
      <xs:complexType name="addInput">
        <xs:sequence>
          <xs:element name="a" type="xs:int" />
          <xs:element name="b" type="xs:int" />
        </xs:sequence>
      </xs:complexType>
      <xs:complexType name="addResponse">
        <xs:sequence>
          <xs:element name="addOutput" type="tns:addOutput" />
        </xs:sequence>
      </xs:complexType>
      <xs:complexType name="addOutput">
        <xs:sequence>
          <xs:element name="result" type="xs:int" />
        </xs:sequence>
      </xs:complexType>
    </xs:schema>
  </wsdl:types>
  <wsdl:message name="add">
    <wsdl:part name="parameters" element="tns:add" />
  </wsdl:message>
  <wsdl:message name="addResponse">
    <wsdl:part name="parameters" element="tns:addResponse" />
  </wsdl:message>
  <wsdl:portType name="CalculatorWS">
    <wsdl:operation name="add">
      <wsdl:input message="tns:add" />
      <wsdl:output message="tns:addResponse" />
    </wsdl:operation>
  </wsdl:portType>
  <wsdl:binding name="CalculatorWSPortBinding" type="tns:CalculatorWS">
    <soap:binding transport="http://schemas.xmlsoap.org/soap/http" style="document" />
    <wsdl:operation name="add">
      <soap:operation soapAction="http://tempuri.org/add" />
      <wsdl:input>
        <soap:body use="literal" />
      </wsdl:input>
      <wsdl:output>
        <soap:body use="literal" />
      </wsdl:output>
    </wsdl:operation>
  </wsdl:binding>
  <wsdl:service name="CalculatorWSService">
    <wsdl:port name="CalculatorWSPort" binding="tns:CalculatorWSPortBinding">
      <soap:address location="http://localhost:8080/WebServices/CalculatorWS" />
    </wsdl:port>
  </wsdl:service>
</wsdl:definitions>

这定义了如下消息:

1
2
3
4
5
6
7
8
9
10
11
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/"
        xmlns:tem="http://tempuri.org">
   <soapenv:Body>
      <tem:add>
         
            ?
            ?
         </addInput>
      </tem:add>
   </soapenv:Body>
</soapenv:Envelope>

和:

1
2
3
4
5
6
7
8
9
10
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/"
       xmlns:tem="http://tempuri.org">
   <soapenv:Body>
      <tem:addResponse>
         
            <result>?</result>
         </addOutput>
      </tem:addResponse>
   </soapenv:Body>
</soapenv:Envelope>

看到package器上的命名空间前缀了吗?那是因为元素是在 http://tempuri.org 命名空间中声明的,而其他元素不在命名空间中。

您甚至可以从命名空间中删除所有元素。从 WSDL 中剥离目标名称空间并使其看起来像这样:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
<?xml version="1.0" encoding="utf-8"?>
<wsdl:definitions
xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/"
name="CalculatorWS">
  <wsdl:types>
    <xs:schema>
      <xs:element name="add" type="add" />
      <xs:element name="addInput" type="addInput" />
      <xs:element name="addResponse" type="addResponse" />
      <xs:element name="addOutput" type="addOutput" />
      <xs:complexType name="add">
        <xs:sequence>
          <xs:element name="addInput" type="addInput" />
        </xs:sequence>
      </xs:complexType>
      <xs:complexType name="addInput">
        <xs:sequence>
          <xs:element name="a" type="xs:int" />
          <xs:element name="b" type="xs:int" />
        </xs:sequence>
      </xs:complexType>
      <xs:complexType name="addResponse">
        <xs:sequence>
          <xs:element name="addOutput" type="addOutput" />
        </xs:sequence>
      </xs:complexType>
      <xs:complexType name="addOutput">
        <xs:sequence>
          <xs:element name="result" type="xs:int" />
        </xs:sequence>
      </xs:complexType>
    </xs:schema>
  </wsdl:types>
  <wsdl:message name="add">
    <wsdl:part name="parameters" element="add" />
  </wsdl:message>
  <wsdl:message name="addResponse">
    <wsdl:part name="parameters" element="addResponse" />
  </wsdl:message>
  <wsdl:portType name="CalculatorWS">
    <wsdl:operation name="add">
      <wsdl:input message="add" />
      <wsdl:output message="addResponse" />
    </wsdl:operation>
  </wsdl:portType>
  <wsdl:binding name="CalculatorWSPortBinding" type="CalculatorWS">
    <soap:binding transport="http://schemas.xmlsoap.org/soap/http" style="document" />
    <wsdl:operation name="add">
      <soap:operation soapAction="http://tempuri.org/add" />
      <wsdl:input>
        <soap:body use="literal" />
      </wsdl:input>
      <wsdl:output>
        <soap:body use="literal" />
      </wsdl:output>
    </wsdl:operation>
  </wsdl:binding>
  <wsdl:service name="CalculatorWSService">
    <wsdl:port name="CalculatorWSPort" binding="CalculatorWSPortBinding">
      <soap:address location="http://localhost:8080/WebServices/CalculatorWS" />
    </wsdl:port>
  </wsdl:service>
</wsdl:definitions>

这个新的 WSDL 将对应如下消息:

1
2
3
4
5
6
7
8
9
10
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/">
   <soapenv:Body>
     
         
            ?
            ?
         </addInput>
      </add>
   </soapenv:Body>
</soapenv:Envelope>

和:

1
2
3
4
5
6
7
8
9
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/">
   <soapenv:Body>
     
         
            <result>?</result>
         </addOutput>
      </addResponse>
   </soapenv:Body>
</soapenv:Envelope>

在这种情况下没有前缀。

现在在两个 WSDL 上都使用 wsimport.exe,您将看到我在开头所说的目标名称空间,即从这里更改:

1
2
3
4
5
6
7
8
9
10
@WebService(name ="CalculatorWS", targetNamespace ="http://tempuri.org")
public interface CalculatorWS {
    @WebMethod(action ="http://tempuri.org/add")
    @WebResult(name ="addOutput", targetNamespace ="")
    @RequestWrapper(localName ="add", targetNamespace ="http://tempuri.org", className ="your.pack.age.Add")
    @ResponseWrapper(localName ="addResponse", targetNamespace ="http://tempuri.org", className ="your.pack.age.AddResponse")
    public AddOutput add(
        @WebParam(name ="addInput", targetNamespace ="")
        AddInput addInput);
}

到这个:

1
2
3
4
5
6
7
8
9
10
@WebService(name ="CalculatorWS", targetNamespace ="")
public interface CalculatorWS {
    @WebMethod(action ="http://tempuri.org/add")
    @WebResult(name ="addOutput", targetNamespace ="")
    @RequestWrapper(localName ="add", targetNamespace ="", className ="your.pack.age.Add")
    @ResponseWrapper(localName ="addResponse", targetNamespace ="", className ="your.pack.age.AddResponse")
    public AddOutput add(
        @WebParam(name ="addInput", targetNamespace ="")
        AddInput addInput);
}

控制 targetNamespace,您将控制消息的外观。