Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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

My web service is not able to process my client's requests when client is calling the web service without passing prefix in the SOAP Body as follows:

<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>

The error is that the Java object corresponding to GetPatientResultsRequest and others are null.

It seems like when there is no prefix in the Body, the deserialization is not happening properly. My web service is able to respond only when the SOAP Body has a prefix like

<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>

Can anyone let me know what to do so that my web service can take SOAP requests of all kinds (i.e. with and without prefix in Body)?

I am using JAX-WS (SOAP 1.1)

like image 367
user1642997 Avatar asked Sep 03 '12 07:09

user1642997


People also ask

What is namespace in SOAP web service?

An XML namespace is a means of qualifying element and attribute names to disambiguate them from other names in the same document. This section provides a brief description of XML namespaces and how they are used in SOAP. For complete information, see http://www.w3.org/TR/REC-xml-names/

What is a namespace prefix?

As a convenience, you can specify a prefix (an alias) for a namespace. A prefix is not an informational part of the element, attribute name, or namespace, and a prefix can be used with both element and attribute names. However, you can specify a default namespace and omit the prefix.

Where is my SOAP namespace?

The SOAP envelope has the namespace identifier "http://schemas.xmlsoap.org/soap/envelope/" The SOAP serialization has the namespace identifier "http://schemas.xmlsoap.org/soap/encoding/"


Video Answer


1 Answers

A web service defines a contract that you must follow in order to call it. Only one message from the examples you posted matches that contract so that one works, the other doesn't.

In your first message you defined a default namespace (because of the xmlns attribute in the wrapper) and all your elements that don't undeclare it and that have no prefix are in the same namespace because they inherit it from their parent.

In your second message you have an explicit prefix declaration and only the wrapper is in that namespaces, the other elements are not in a namespace and don't inherit a default one from the parent (because of xmlns attribute missing).

As I said at the beginning, the web service defines a contract. It makes more sense to modify the clients to send the correct message instead of changing the service to accept incorrect messages from the client.

To control the namespaces of your elements you need to use the targetNamespace values on the JAX-WS annotations of your web service and client.

Here is an example to see the difference in code and message format when you change the target namespaces. I'll use a basic WSDL for this:

<?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>

This defines messages like:

<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/"
        xmlns:tem="http://tempuri.org">
   <soapenv:Body>
      <tem:add>
         <addInput>
            <a>?</a>
            <b>?</b>
         </addInput>
      </tem:add>
   </soapenv:Body>
</soapenv:Envelope>

and:

<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/"
       xmlns:tem="http://tempuri.org">
   <soapenv:Body>
      <tem:addResponse>
         <addOutput>
            <result>?</result>
         </addOutput>
      </tem:addResponse>
   </soapenv:Body>
</soapenv:Envelope>

See the namespace prefix on the wrappers? That's because the elements are declared in the http://tempuri.org namespace while the others are not and are not in a namespace.

You can even remove all elements from the namespaces. Strip the target namespace from the WSDL and get it to look like this:

<?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>

This new WSDL will correspond to messages like:

<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/">
   <soapenv:Body>
      <add>
         <addInput>
            <a>?</a>
            <b>?</b>
         </addInput>
      </add>
   </soapenv:Body>
</soapenv:Envelope>

and:

<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/">
   <soapenv:Body>
      <addResponse>
         <addOutput>
            <result>?</result>
         </addOutput>
      </addResponse>
   </soapenv:Body>
</soapenv:Envelope>

No prefix in this case.

Now use wsimport.exe on both WSDLs and you will see the target namespaces I was talking about at the beginning, namely a change from this:

@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);
}

to this:

@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);
}

Control the targetNamespace and you will control how the message looks.

like image 124
Bogdan Avatar answered Oct 23 '22 04:10

Bogdan