【问题标题】:Consume Axis2 Web Services in Visual Studio 2008在 Visual Studio 2008 中使用 Axis2 Web 服务
【发布时间】:2009-06-09 08:58:40
【问题描述】:

我在让 Visual Studio 与我的 Axis2 Web 服务完美配合时遇到了一些麻烦。这个问题很奇怪,虽然不是一个表演障碍。如果有的话,它只是烦人,我真的很感激能解决这个问题。

问题在于,在 C# 代码中使用 Web 服务时,所有参数或返回值都不是其本机数据类型。因此,不仅仅是能够调用,例如:

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

我要写

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

如您所知,这很快就会变得非常烦人。奇怪的是,在创建 Web 服务的 Web 引用时,所有数据类型和参数都正确显示在服务发现页面中。我已尝试修改 Web 服务的 WSDL 文件以删除任何可能使 Visual Studio 感到困惑的内容,但到目前为止,我还无法让它正常工作。

我在某处读到这是反序列化过程中的 Visual Studio 和/或 .Net 问题,而不是 Web 服务本身的问题。我认为这可能是真的,因为 Web 服务可以在 NetBeans 中正确使用。

Web Service 使用 Java 编写并托管在axis2 / Tomcat 服务器上,但客户端软件将使用 C# .Net 2.0 编写。

无论如何 - 以前有人经历过这种情况吗?我一直无法找到我再次阅读有关反序列化问题的页面,所以如果有人有什么可以帮助我的,我将非常感激。

【问题讨论】:

    标签: java .net visual-studio web-services axis2


    【解决方案1】:

    我建议您使用 document/literal/wrapped 样式定义您的 WSDL,据我所知,当您需要互操作性时,这似乎是最合适的。

    这使您的服务实现在 WCF 中表现良好,Viusual Studio 2008 在您定义服务引用时使用它。

    可以在不破坏现有实现的情况下修改服务的 WSDL 规范,但不要指望它。

    不过,棘手的部分是您必须在 WSDL 中使用一些特殊的术语,这样 WCF 才不会按您的要求生成漂亮的包装器。在您的情况下,自动生成的客户端代码似乎回退到文档/文字样式,您可以在其中创建并初始化您提供给客户端服务方法的“结构”。

    本质上,你需要做的是:

    1. 使用 XML Schema 元素定义您的 wsdl:types。
    2. 在定义类型时坚持合理的 XML Schema 结构子集(有关列表,请参阅 WCF 文档)。
    3. object-type 的所有元素(例如 xsd:token、xsd:NMTOKEN、xsd:base64Binary、...)声明为 nillable="true" - 普通类型,例如 xsd:int 应该可以为空。
    4. 做好准备,序列中用作答案的 first 元素将从服务调用返回,而其余元素将作为输出参数传递 - 这通常使 xsd:int 状态成为合适的候选人在序列中排名第一。
    5. 使用名为 parameters 的 wsdl:part 定义您的 wsdl:message(不是参数,不是参数,它必须是参数)并使用 element 属性
    6. 将 wsdl:binding 中的 soap:operation 的样式定义为“document”。
    7. 声明 wsdl:input 和 wsdl:output 以使用“文字”SOAP 编码。

    使用您的 WSDL 文件并使用 svcutil 生成客户端代码。如果您的 WSDL 文件中有错误,您将收到警告,如果您查看生成的代码,您将看到可能指出包装样式失败原因的 cmets。

    这里可能会用到一些代码。这是一个精简的 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 来实现该服务。首先,我们需要一个服务规范(AAR 中的 services.xml):

    <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 上。顺便说一句,我们在服务配置中提到的“Adressing”模块通常是默认添加的,但是还有其他模块可能用于处理WS-I标准的其他部分。

    【讨论】:

      【解决方案2】:

      我在某处读到这是一个视觉 Studio 和/或 .Net 中的问题 反序列化过程,而不是 Web 服务本身的问题。 我想这可能是真的,因为 可以正确使用 web 服务 在 NetBeans 中。

      我以前遇到过这个问题 - 这是一个 .Net 问题。我的方法是对蚂蚁大锤,并在.Net中重新编写服务

      【讨论】:

      • 更新到axis2似乎有所帮助,但核心问题似乎仍然存在 - 现在.Net不会正确地将参数发送到Web服务,并且无法接收返回值。不幸的是,在 .Net 中重写 Web 服务对我们来说是不可能的,因为 Web 服务需要在 Linux 服务器上运行。我想我可以使用 Mono - 但我真的不想走那条路。
      • 您是否尝试过手动检查 WSDL 和所有内容?有时axis2wsdl会错过一些东西。忘记将某些东西设为数组或给出错误的值
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-05-15
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2010-10-27
      • 1970-01-01
      相关资源
      最近更新 更多