java - 在 Visual Studio 2008 中使用 Axis2 Web 服务

标签 java .net visual-studio web-services apache-axis

我在让 Visual Studio 与我的 Axis2 Web 服务完美配合时遇到了一些麻烦。这个问题很奇怪,虽然不是一个问题。如果有的话,那就是很烦人,我真的很感激有一种解决方法。

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

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 服务本身的问题。我认为这可能是真的,因为可以在 NetBeans 中正确使用 Web 服务。

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

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

最佳答案

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

这使得您的服务实现与 WCF 表现良好,当您定义服务引用时,Viusual Studio 2008 将使用 WCF。

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

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

本质上,您需要做的是:

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

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

一些代码可能在这里有用。这是一个精简的 WSDL,使用一个 GetVersionInformation 方法来描述服务,该方法返回三元组 {"1.0", 1, 0} - 实际上是使用主要版本号和次要版本号的接口(interface)版本。

<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 标准的其他部分。

关于java - 在 Visual Studio 2008 中使用 Axis2 Web 服务,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/968957/

相关文章:

java - 将图像插入边框作为背景

java - ConcurrentHashMap 是否需要包装在同步块(synchronized block)中?

java - 在 Java 记录中强制执行不可变集合?

c++ - 判断 C++ 代码是否可移植

c++ - Windows Visual C++ 链接到 Mesa3D OpenGL

visual-studio - VS 2017错误:“正在进行另一次编辑时尝试了TextBuffer编辑操作”

java - 将java表导出到excel时出现问题

.net - dotnet aspnetcore docker build 失败并显示 145 错误代码

.net - F#尾递归调用

.net - linq 查询中的 String.Format