Использование веб-служб Axis2 в Visual Studio 2008 - PullRequest
0 голосов
/ 09 июня 2009

У меня возникли некоторые проблемы с тем, чтобы Visual Studio хорошо играл с моим веб-сервисом Axis2. Проблема очень странная, хотя и не выставочная заглушка. Во всяком случае, это просто раздражает, и я очень ценю способ обойти это.

Проблема в том, что при использовании веб-службы в коде C # ни один из параметров или возвращаемых значений не имеет своих собственных типов данных. Поэтому вместо того, чтобы просто звонить, например:

string timeDiff = MyWebService.GetServerTimeDifference(LocalTime)
Console.WriteLine(timeDiff);

Я должен написать

MyWebService.GetServerTimeDifference request = new MyWebService.GetServerTimeDifference();
request.@LocalTime = LocalTime;
Console.WriteLine(MyWebService.GetServerTimeDifference(request).@return);

Как вы можете сказать, это очень быстро раздражает. Странно то, что при создании веб-ссылки на веб-службу все типы данных и параметры правильно отображаются на странице обнаружения службы. Я попытался изменить файл WSDL для веб-службы, чтобы удалить все, что может сбить с толку Visual Studio, но до сих пор я не смог заставить его работать так, как должно.

Я где-то читал, что это проблема Visual Studio и / или .Net в процессе десериализации, а не проблема самого веб-сервиса. Я думаю, что это может быть правдой, поскольку веб-служба может правильно использоваться в NetBeans.

Веб-служба написана на Java и размещена на сервере axis2 / Tomcat, но клиентское программное обеспечение будет написано на C # .Net 2.0.

В любом случае - кто-нибудь испытывал это раньше? Мне не удалось найти страницу, на которой я снова читал о проблеме десериализации, поэтому, если кто-то хочет что-то сказать, что может мне помочь, я очень признателен.

Ответы [ 2 ]

4 голосов
/ 22 сентября 2009

Я предлагаю вам определить свой WSDL, используя стиль document / literal / wrapped , который, насколько я знаю, кажется наиболее подходящим, когда вам нужна совместимость.

Благодаря этому реализация службы работает хорошо с WCF, который используется Viusual Studio 2008 при определении ссылки на службу.

Можно изменить спецификацию WSDL для вашего сервиса, не нарушая существующую реализацию, но не рассчитывайте на это.

Сложность заключается в том, что вам нужно использовать какой-то особый жаргон в своем WSDL, чтобы WCF не избавлялся от создания хороших оболочек, как вы и просили. В вашем случае автоматически сгенерированный клиентский код, похоже, возвращается к стилю документа / литерала, где вы создаете и инициализируете "структуры", которые вы передаете методу обслуживания клиентов.

По сути, вам нужно:

  1. Определите ваши типы wsdl: используя XML-схему element .
  2. При определении типов придерживайтесь разумного подмножества конструкций XML-схемы (список см. В документации WCF).
  3. Объявить все элементы объектного типа (например, xsd: токен, xsd: NMTOKEN, xsd: base64Binary, ...) как nillable = "true" - простые типы , такие как xsd: int, должны not быть nillable.
  4. Будьте готовы к тому, что первый элемент в последовательности, используемой в качестве ответа, будет возвращен из вызова службы, а остальные будут переданы в качестве параметров out - это обычно делает состояние xsd: int подходящим кандидатом как первый в последовательности.
  5. Определите ваше wsdl: сообщение, используя wsdl: part с именем параметры (не параметр, не param, это должны быть параметры) и используя атрибут element
  6. Определите стиль операции soap: в wsdl: binding как «документ».
  7. Объявите wsdl: input и wsdl: output для использования "буквальной" кодировки SOAP.

Поиграйте с файлом WSDL и используйте svcutil для генерации кода клиента. Вы получите предупреждения, если в вашем файле WSDL есть ошибки, и если вы посмотрите на сгенерированный код, вы увидите комментарии, которые могут указывать на то, почему стиль упаковки не работает.

Здесь может быть полезен некоторый код. Это урезанный WSDL, описывающий сервис с одним методом GetVersionInformation, который возвращает триплет {"1.0", 1, 0} - фактически являющийся версией интерфейса с использованием старшего и младшего номеров версий.

<wsdl:definitions 
    targetNamespace="http://tempuri.org"
    xmlns:tns="http://tempuri.org"
    xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/"
 xmlns:soap12="http://schemas.xmlsoap.org/wsdl/soap12/"
 xmlns:http="http://schemas.xmlsoap.org/wsdl/http/" 
 xmlns:mime="http://schemas.xmlsoap.org/wsdl/mime/" 
 xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/">

 <wsdl:types>
  <xsd:schema
            elementFormDefault="qualified"
            targetNamespace="http://tempuri.org"
            xmlns:xsd="http://www.w3.org/2001/XMLSchema">

            <!-- Wrapper elements. Conventions apply to wrapper element names! -->
            <xsd:element name="GetVersionInformation" nillable="true" type="tns:VoidType" />
            <xsd:element name="GetVersionInformationResponse" nillable="true" type="tns:VersionInformationType" />

            <!-- Just a void type -->
            <xsd:complexType name="VoidType">
                <xsd:sequence />
            </xsd:complexType>

            <!-- Major and minor version information -->
            <xsd:complexType name="VersionInformationType">
                <xsd:sequence>
                    <xsd:element nillable="true" minOccurs="1" maxOccurs="1" name="version" type="xsd:NMTOKEN" />
                    <xsd:element minOccurs="1" maxOccurs="1" name="major" type="xsd:int" />
                    <xsd:element minOccurs="1" maxOccurs="1" name="minor" type="xsd:int" />
                </xsd:sequence>
            </xsd:complexType>

  </xsd:schema>
 </wsdl:types>



    <!-- GetVersionInformation -->
    <wsdl:message name="GetVersionInformationSoapIn">
        <wsdl:part name="parameters" element="tns:GetVersionInformation" />
    </wsdl:message>

    <wsdl:message name="GetVersionInformationSoapOut">
        <wsdl:part name="parameters" element="tns:GetVersionInformationResponse" />
    </wsdl:message>




    <!-- Port type -->
 <wsdl:portType name="MyServicePortType">
        <wsdl:operation name="GetVersionInformation">
            <wsdl:input message="tns:GetVersionInformationSoapIn" />
            <wsdl:output message="tns:GetVersionInformationSoapOut" />
        </wsdl:operation>
 </wsdl:portType>



 <wsdl:binding name="MyServiceSOAP11Binding" type="tns:MyServicePortType">
  <soap:binding transport="http://schemas.xmlsoap.org/soap/http" style="document" />
        <wsdl:operation name="GetVersionInformation">
            <wsdl:input>
                <soap:body use="literal" parts="parameters" />
            </wsdl:input>
            <wsdl:output>
                <soap:body use="literal" />
            </wsdl:output>
        </wsdl:operation>
 </wsdl:binding>



 <wsdl:binding name="MyServiceSOAP12Binding" type="tns:MyServicePortType">
  <soap12:binding transport="http://schemas.xmlsoap.org/soap/http" style="document" />
        <wsdl:operation name="GetVersionInformation">
            <wsdl:input>
                <soap12:body use="literal" parts="parameters" />
            </wsdl:input>
            <wsdl:output>
                <soap12:body use="literal" />
            </wsdl:output>
        </wsdl:operation>
 </wsdl:binding>



 <wsdl:service name="MyService">
  <wsdl:port name="MyServiceSOAP11port" binding="tns:MyServiceSOAP11Binding">
   <soap:address location="http://localhost:80/mojo/services/MyService" />
  </wsdl:port>

  <wsdl:port name="MyServiceSOAP12port" binding="tns:MyServiceSOAP12Binding">
   <soap12:address location="http://localhost:80/mojo/services/MyService" />
  </wsdl:port>
 </wsdl:service>
</wsdl:definitions>

Вы можете сгенерировать код клиента для этого WSDL, используя:

  svcutil /t:code /serializer:DataContractSerializer /s  /out:MyService.cs /n:*,MyService /ixt MyService.wsdl

Далее, вызов этого кода из C # будет выглядеть так:

// The endpointConfigurationName must match the corresponding entry
// in app.config, with the following content:
//
//    <configuration>
//        <system.serviceModel>
//            <bindings>
//                <basicHttpBinding>
//                    <binding name="MyServiceSOAP11Binding" ...>
//                    </binding>
//                    .../...
//                </basicHttpBinding>
//            </bindings>
//            <client>
//                <endpoint 
///                  ... binding="basicHttpBinding" 
//                    ... bindingConfiguration="MyServiceSOAP11Binding"
//                    ... name="MyServiceSOAP11port" />
//            </client>
//        </system.serviceModel>
//    </configuration> 
//
string endpointConfigurationName = "MyServiceSOAP11port";
string wsEndpoint = "http://localhost/mojo/services/MyService";

MyService.MyServicePortTypeClient wsClient = null;
try
{
 wsClient = new MyService.MyServicePortTypeClient(endpointConfigurationName, wsEndpoint);
}
catch (InvalidOperationException ioe)
{
 // Possibly a problem with the configuration
 // Inform(Logging.LogLevel.WARNING, "Potential problem with configuration: " + ioe.Message);
 return;
}

string wsUsername = "John";
string wsPassword = "Doe";

if (!String.IsNullOrEmpty(wsUsername) && !String.IsNullOrEmpty(wsPassword))
{
 UserNamePasswordClientCredential credentials = wsClient.ClientCredentials.UserName;
 credentials.UserName = wsUsername;
 credentials.Password = wsPassword;
}

try
{
 int major;
 int minor;
 string version = wsClient.GetVersionInformation(out major, out minor);
 // Inform(Logging.LogLevel.DEBUG, "Service has version " + version);
}
catch (System.ServiceModel.EndpointNotFoundException enfe)
{
 // string info = "Could not contact MyService: " + enfe.Message;
 // Inform(Logging.LogLevel.ERROR, info);
 return;
}
catch (System.ServiceModel.FaultException fe)
{
 // string info = "Could not contact MyService: " + fe.Message;
 // Inform(Logging.LogLevel.ERROR, info);
 return;
}

Пока мы занимаемся этим, почему бы не реализовать сервис с использованием Axis2. Во-первых, нам нужна спецификация сервиса (services.xml в нашем AAR):

<serviceGroup name="MyServices">

    <service name="MyService" scope="application">
        <description>My Service - document/literal wrapped style, suited for .NET integration</description>

        <!-- Service methods -->
        <operation name="GetVersionInformation">
            <messageReceiver class="org.apache.axis2.receivers.RawXMLINOutMessageReceiver"/>
            <actionMapping>http://tempuri.org/MyServicePortType/GetVersionInformationRequest</actionMapping>
        </operation>

        <!-- Use WS-Adressing, ... -->
        <module ref="addressing" />

        <!-- Service implementation -->
        <parameter name="ServiceClass">com.mycompany.services.MyService</parameter>
    </service>

    <service name="MyOtherService" scope="application" >
  .../...
    </service>

</serviceGroup>

И наша реализация сервера, используя AXIOM:

package com.mycompany.services.MyService;

import javax.xml.stream.XMLStreamException;
import javax.xml.namespace.QName;

import org.apache.axiom.om.*;
import org.apache.axis2.context.ServiceContext;
import org.apache.log4j.Logger;



public class MyService {
    public static final Integer MAJOR_VERSION = 1;
    public static final Integer MINOR_VERSION = 0;

    public static final String NAMESPACE = "http://tempuri.org";
    public static final String NAMESPACE_ALIAS = "tns";
    public static final String GET_VERSION_INFORMATION_RESPONSE_ELEMENT_NAME = "GetVersionInformationResponse";

    private ServiceContext serviceContext = null;
    private String serviceName = null;

    private static final Logger log = Logger.getLogger("SERVICE");

    public void init(ServiceContext serviceContext) throws Exception {
        this.serviceContext = serviceContext;
        serviceName = serviceContext.getName();
    }

    public OMElement GetVersionInformation(OMElement element) throws XMLStreamException {
        // --- Handle request ---
        String version = "" + MAJOR_VERSION + "." + MINOR_VERSION;

        if (log.isDebugEnabled()) {
            log.debug("Retrieving version information: " + version);
        }

        // --- Prepare response ---
        OMFactory factory = OMAbstractFactory.getOMFactory();
        OMNamespace omNs = factory.createOMNamespace(NAMESPACE, NAMESPACE_ALIAS);

        //
        OMElement response = factory.createOMElement(GET_VERSION_INFORMATION_RESPONSE_ELEMENT_NAME, omNs);
        {
            OMElement value;
            {
                value = factory.createOMElement("version", omNs);
                value.addChild(factory.createOMText(value, version));
                response.addChild(value);
            }
            {
                value = factory.createOMElement("major", omNs);
                value.addChild(factory.createOMText(value, "" + MAJOR_VERSION));
                response.addChild(value);
            }
            {
                value = factory.createOMElement("minor", omNs);
                value.addChild(factory.createOMText(value, "" + MINOR_VERSION));
                response.addChild(value);
            }
        }
        return response;
    }
}

С Axis2 и Axiom действительно приятно работать. Если у вас возникли проблемы при создании клиента C #, повторно посетите WSDL - вряд ли проблема связана с Axis2. Кстати, модуль «Адресация», на который мы ссылаемся в конфигурации сервиса, обычно добавляется по умолчанию, но есть и другие модули, которые могут использоваться для обработки других частей стандарта WS-I.

0 голосов
/ 11 июня 2009

Я где-то читал, что это Visual Студия и / или .Net проблема в процесс десериализации, а не проблема с самим веб-сервисом. Я думаю, что это может быть правдой, так как веб-сервис может быть использован правильно в NetBeans.

У меня раньше была эта проблема - это проблема .Net. Мой подход состоял в том, чтобы взять кувалду к муравью и переписать службу в .Net

...