web-dev-qa-db-fra.com

SOAP WS - rendre @WebParam facultatif

J'ai une méthode assez simple, que j'utilise dans les annotations JAX-WS de l'API WS:

@WebMethod
public MyResponse sendSingle2(
    @WebParam(name="username") String username,
    @WebParam(name="password") String password,
    @WebParam(name="newParam") String newParam) {
        // the code
    }

Maintenant, je veux que newParam soit optionnel. Je veux dire que je veux que la méthode fonctionne toujours non seulement lorsque le paramètre est vide dans le fichier XML passé:

<ws:sendSingle2>
    <username>user</username>
    <password>pass</password>
    <newParam></newParam>
</ws:sendSingle2>

mais aussi quand il est absent:

<ws:sendSingle2>
    <username>user</username>
    <password>pass</password>
</ws:sendSingle2>

J'en ai besoin pour ne pas casser l'API existante, qui fonctionne sans le nouveau paramètre.

12
amorfis

@WebParam mappe une partie du message sur un paramètre et ne peut pas être optionnel. Voir Parties de message facultatives dans WSDL . Par conséquent, la réponse courte est que ce que vous demandez n'est pas possible. Mais si vous pouvez refactoriser cette méthode, vous pouvez utiliser l’une des approches décrites ci-dessous.

Généralement, le caractère optionnel d'un paramètre est défini via le schéma minOccurs=0. En outre, au lieu d’utiliser plusieurs paramètres, vous pouvez définir un paramètre Request dans votre schéma, que vous définissez comme paramètre pour votre WebMethod. Le caractère facultatif est maintenant encapsulé dans le paramètre et la même méthode est invoquée pour un appel avec ou sans le ou les paramètres facultatifs.

Je préfère commencer par définir le contrat plutôt que de compter sur des fichiers générés automatiquement. Une fois que vous avez compris comment XSD, SOAP et WSDL jouent ensemble, vous ne souhaitez plus utiliser les définitions basées sur les annotations/premier code, car vous êtes plus flexible en sens inverse.

Exemple de code:

<xs:schema
    targetNamespace="http://your.namespace.com"
    xmlns:tns="http://your.namespace.com"
    xmlns:xs="http://www.w3.org/2001/XMLSchema"
    elementFromDefault="qualified"
    attributeFromDefault="qualified">

...

<xs:element name="MyRequest" type="tns:MyRequestType" />
<xs:element name="MyResponse" type="tns:MyResponseType" />

<xs:complexType name"MyRequestType">
    <xs:sequence>
        <xs:element name="username" type="xs:string" minOccurs="1" maxOccurs="1" />
        <xs:element name="password" type="xs:string" minOccurs="1" maxOccurs="1" />
        <xs:element name="newParam" type="xs:string" minOccurs="0" maxOccurs="1" />
    </xs:sequence>
</xs:complexType>

...

</xs:schema>

Dans votre fichier WSDL, vous définissez le message comme suit:

<wsdl:definitions
    xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/"
    xmlns:msg="http://your.namespace.com"
    xmlns:xs="http://www.w3.org/2001/XMLSchema"
    xmlns:http="http://schemas.xmlsoap.org/wsdl/http/"
    xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/"
    xmlns:soapenc="http://schemas.xmlsoap.org/soap/encoding/"
    targetNamespace="http://your.namespace.com">

    <wsdl:types>
        <xs:schema>
            <!-- either import the externalized schema -->
            <xs:import namespace="http://your.namespace.com"
                       schemaLocation="someDir/yourMessageSchema.xsd" />
        </xs:schema>
        <!-- or define the schema within the WSDL - just copy the schema here -->
        <xs:schema
            targetNamespace="http://your.namespace.com"
            xmlns:tns="http://your.namespace.com"
            xmlns:xs="http://www.w3.org/2001/XMLSchema"
            elementFromDefault="qualified"
            attributeFromDefault="qualified">
                ...
        </xs:schema>
    </wsdl:types>

    ...

    <wsdl:message name="sendSingle2Request">
        <wsdl:part name="in" element="msg:MyRequest" />
    </wsdl:message>

    <wsdl:message name="sendSingle2Response">
        <wsdl:part name="out" element="msg:MyResponse" />
    </wsdl:message>

    ...

    <wsdl:portType name="YourServiceEndpoint">
        <wsdl:operation name="sendSingle2">
            <wsdl:input message="tns:sendSingle2Request" />
            <wsdl:output message="tns:sendSingle2Response" />
        </wsdl:operation>
        ...
    </wsdl:portType>

    <wsdl:binding name="YourServiceBinding" type="YourServiceEndpoint">
        <soap:binding style="document" transport="http://schemas.xmlsoap.org/soap/http" />
        <wsdl:operation name=""sendSingle2">
            <soap:operation soapAction="http://your.namespace.com/SendSingle2" style="document" />
            <wsdl:input>
                <soap:body parts="in" use="literal" />
            </wsdl:input>
            <wsdl:output>
                <soap:body parts="out" use="literal" />
            </wsdl:output>
        </wsdl:operation>
        ...
    </wsdl:binding>

    <wsdl:service name="YourService">
        <wsdl:port name="YourServicePort binding="tns:YourServiceBinding">
            <soap:address location="http://your.server:port/path/to/the/service" />
        </wsdl:port>
    </wsdl:service>
</wsdl:definitions>

Le contrat WSDL définit ici l’utilisation du style: document/literal et, à l’aide du schéma, le message SOAP réel sera document/literal wrapped, qui est en outre conforme à WS-I.

Votre méthode passera donc à public MyResponse sendSinge2(MyRequest request)request encapsulera maintenant username, passowrd et newParam. Si newParam n'a pas été envoyé avec la demande SOAP, il retournera simplement null, il est donc préférable de vérifier avant de l'utiliser.

Si vous vous en tenez à l'approche code d'abord, vous devrez d'abord définir votre classe MyRequest que vous utiliserez comme paramètre de requête au lieu de ces 2 ou 3 valeurs.

@XmlAccessorType(XmlAccessType.FIELD)
@XmlRootElement(name = "MyRequest", namespace="http://your.namespace.com")
public class MyRequest implements Serializable
{
   @XmlElement(name = "username", required = true)
   protected String username;
   @XmlElement(name = "password", required = true)
   protected String password;
   @XmlElement(name = "newParam", required = false)
   protected String newParam;
   ...
}

La même chose devrait être faite pour MyResult si vous ne l'avez pas encore fait. La méthode web peut maintenant ressembler à quelque chose comme ça:

@WebMethod(operationName = "sendSingle2")
@WebResult(name = "sendSingle2Response", targetNamespace = "http://your.namespace.com")
public MyResult sendSingle2(@WebParam(name = "sendSingle2Request") MyRequest request)
{
   ...
}

Encore une fois, request encapsule les 3 paramètres pour lesquels vous devez vérifier si les paramètres facultatifs sont nuls.

HTH

15
Roman Vottner

Tout dépend de votre classe d'implémentation, où vous utilisez ces paramètres . Sur l'interface de point de terminaison, ajoutez simplement ce paramètre en tant que webparam. 

Assurez-vous que dans votre classe d'implémentation Si vous utilisez ce paramètre où que vous soyez, ajoutez également du code alternatif (partie Else) pour effectuer votre opération ou exécution sans ce paramètre également.

Je pense que tous les paramètres sont facultatifs jusqu'à ce que vous les validiez ou les utilisiez dans votre classe d'implémentation, comme recommandé.

0
kingAm