使用 WCF WsdlExporter 如何创建与 singleWsdl 完全一样的 WSDL?

Posted

技术标签:

【中文标题】使用 WCF WsdlExporter 如何创建与 singleWsdl 完全一样的 WSDL?【英文标题】:With WCF WsdlExporter how to create a WSDL exactly like singleWsdl? 【发布时间】:2020-11-20 08:20:32 【问题描述】:

当我创建 WCF 服务应用程序时,我得到了一个具有两种方法的简单服务。如果我运行它,我会得到一个页面,我可以在该页面上单击 singleWsdl 页面。 WSDL 超过 100 行,适用于 SoapUI 和 WcfTestClient。

现在我想使用 WslExporter 创建完全相同的 WSDL。 (见:https://docs.microsoft.com/en-us/dotnet/api/system.servicemodel.description.wsdlexporter?view=netframework-4.8)

我的尝试:

WsdlExporter exporter = new WsdlExporter();
exporter.PolicyVersion = PolicyVersion.Policy15;
var myContract = ContractDescription.GetContract(typeof(IService1));
exporter.ExportContract(myContract);
exporter.ExportEndpoint(
    new ServiceEndpoint(myContract, new BasicHttpBinding(), new EndpointAddress("http://x.y.z/Service1.svc")));

string wsdl;
using (var sw = new StringWriter())

    exporter.GeneratedWsdlDocuments[0].Write(sw);
    wsdl = sw.ToString();

return wsdl;

但结果是行数少得多的 WSDL。如果在 WcfTestClient 的 SoapUI 中加载它会产生错误。那么...如何创建一个有效的 WSDL?

*** 编辑 ***

在 Visual Studio 2017 中,我创建了一个新项目:WCF 服务应用程序 这个空项目的一部分是下一个类,在合约中使用:

    [DataContract]
    public class CompositeType
    
        bool boolValue = true;
        string stringValue = "Hello ";

        [DataMember]
        public bool BoolValue
        
            get  return boolValue; 
            set  boolValue = value; 
        

        [DataMember]
        public string StringValue
        
            get  return stringValue; 
            set  stringValue = value; 
        
    

如果我启动它,我会得到这个 WSDL:

<wsdl:definitions xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/" xmlns:wsam="http://www.w3.org/2007/05/addressing/metadata" xmlns:wsx="http://schemas.xmlsoap.org/ws/2004/09/mex" xmlns:wsap="http://schemas.xmlsoap.org/ws/2004/08/addressing/policy" xmlns:msc="http://schemas.microsoft.com/ws/2005/12/wsdl/contract" xmlns:wsp="http://schemas.xmlsoap.org/ws/2004/09/policy" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/" xmlns:wsu="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd" xmlns:soap12="http://schemas.xmlsoap.org/wsdl/soap12/" xmlns:soapenc="http://schemas.xmlsoap.org/soap/encoding/" xmlns:tns="http://tempuri.org/" xmlns:wsa10="http://www.w3.org/2005/08/addressing" xmlns:wsaw="http://www.w3.org/2006/05/addressing/wsdl" xmlns:wsa="http://schemas.xmlsoap.org/ws/2004/08/addressing" name="Service1" targetNamespace="http://tempuri.org/">
    <wsdl:types>
        <xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema" elementFormDefault="qualified" targetNamespace="http://tempuri.org/">
            <xs:import namespace="http://schemas.datacontract.org/2004/07/Turien.Dynamic.Basic"/>
            <xs:element name="GetData">
                <xs:complexType>
                    <xs:sequence>
                        <xs:element minOccurs="0" name="value" type="xs:int"/>
                    </xs:sequence>
                </xs:complexType>
            </xs:element>
            <xs:element name="GetDataResponse">
                <xs:complexType>
                    <xs:sequence>
                        <xs:element minOccurs="0" name="GetDataResult" nillable="true" type="xs:string"/>
                    </xs:sequence>
                </xs:complexType>
            </xs:element>
            <xs:element name="GetDataUsingDataContract">
                <xs:complexType>
                    <xs:sequence>
                        <xs:element xmlns:q1="http://schemas.datacontract.org/2004/07/Turien.Dynamic.Basic" minOccurs="0" name="composite" nillable="true" type="q1:CompositeType"/>
                    </xs:sequence>
                </xs:complexType>
            </xs:element>
            <xs:element name="GetDataUsingDataContractResponse">
                <xs:complexType>
                    <xs:sequence>
                        <xs:element xmlns:q2="http://schemas.datacontract.org/2004/07/Turien.Dynamic.Basic" minOccurs="0" name="GetDataUsingDataContractResult" nillable="true" type="q2:CompositeType"/>
                    </xs:sequence>
                </xs:complexType>
            </xs:element>
        </xs:schema>
        <xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:tns="http://schemas.microsoft.com/2003/10/Serialization/" attributeFormDefault="qualified" elementFormDefault="qualified" targetNamespace="http://schemas.microsoft.com/2003/10/Serialization/">
            <xs:element name="anyType" nillable="true" type="xs:anyType"/>
            <xs:element name="anyURI" nillable="true" type="xs:anyURI"/>
            <xs:element name="base64Binary" nillable="true" type="xs:base64Binary"/>
            <xs:element name="boolean" nillable="true" type="xs:boolean"/>
            <xs:element name="byte" nillable="true" type="xs:byte"/>
            <xs:element name="dateTime" nillable="true" type="xs:dateTime"/>
            <xs:element name="decimal" nillable="true" type="xs:decimal"/>
            <xs:element name="double" nillable="true" type="xs:double"/>
            <xs:element name="float" nillable="true" type="xs:float"/>
            <xs:element name="int" nillable="true" type="xs:int"/>
            <xs:element name="long" nillable="true" type="xs:long"/>
            <xs:element name="QName" nillable="true" type="xs:QName"/>
            <xs:element name="short" nillable="true" type="xs:short"/>
            <xs:element name="string" nillable="true" type="xs:string"/>
            <xs:element name="unsignedByte" nillable="true" type="xs:unsignedByte"/>
            <xs:element name="unsignedInt" nillable="true" type="xs:unsignedInt"/>
            <xs:element name="unsignedLong" nillable="true" type="xs:unsignedLong"/>
            <xs:element name="unsignedShort" nillable="true" type="xs:unsignedShort"/>
            <xs:element name="char" nillable="true" type="tns:char"/>
            <xs:simpleType name="char">
                <xs:restriction base="xs:int"/>
            </xs:simpleType>
            <xs:element name="duration" nillable="true" type="tns:duration"/>
            <xs:simpleType name="duration">
                <xs:restriction base="xs:duration">
                    <xs:pattern value="\-?P(\d*D)?(T(\d*H)?(\d*M)?(\d*(\.\d*)?S)?)?"/>
                    <xs:minInclusive value="-P10675199DT2H48M5.4775808S"/>
                    <xs:maxInclusive value="P10675199DT2H48M5.4775807S"/>
                </xs:restriction>
            </xs:simpleType>
            <xs:element name="guid" nillable="true" type="tns:guid"/>
            <xs:simpleType name="guid">
                <xs:restriction base="xs:string">
                    <xs:pattern value="[\da-fA-F]8-[\da-fA-F]4-[\da-fA-F]4-[\da-fA-F]4-[\da-fA-F]12"/>
                </xs:restriction>
            </xs:simpleType>
            <xs:attribute name="FactoryType" type="xs:QName"/>
            <xs:attribute name="Id" type="xs:ID"/>
            <xs:attribute name="Ref" type="xs:IDREF"/>
        </xs:schema>
        <xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:tns="http://schemas.datacontract.org/2004/07/Turien.Dynamic.Basic" elementFormDefault="qualified" targetNamespace="http://schemas.datacontract.org/2004/07/Turien.Dynamic.Basic">
            <xs:complexType name="CompositeType">
                <xs:sequence>
                    <xs:element minOccurs="0" name="BoolValue" type="xs:boolean"/>
                    <xs:element minOccurs="0" name="StringValue" nillable="true" type="xs:string"/>
                </xs:sequence>
            </xs:complexType>
            <xs:element name="CompositeType" nillable="true" type="tns:CompositeType"/>
        </xs:schema>
    </wsdl:types>
    <wsdl:message name="IService1_GetData_InputMessage">
        <wsdl:part name="parameters" element="tns:GetData"/>
    </wsdl:message>
    <wsdl:message name="IService1_GetData_OutputMessage">
        <wsdl:part name="parameters" element="tns:GetDataResponse"/>
    </wsdl:message>
    <wsdl:message name="IService1_GetDataUsingDataContract_InputMessage">
        <wsdl:part name="parameters" element="tns:GetDataUsingDataContract"/>
    </wsdl:message>
    <wsdl:message name="IService1_GetDataUsingDataContract_OutputMessage">
        <wsdl:part name="parameters" element="tns:GetDataUsingDataContractResponse"/>
    </wsdl:message>
    <wsdl:portType name="IService1">
        <wsdl:operation name="GetData">
            <wsdl:input wsaw:Action="http://tempuri.org/IService1/GetData" message="tns:IService1_GetData_InputMessage"/>
            <wsdl:output wsaw:Action="http://tempuri.org/IService1/GetDataResponse" message="tns:IService1_GetData_OutputMessage"/>
        </wsdl:operation>
        <wsdl:operation name="GetDataUsingDataContract">
            <wsdl:input wsaw:Action="http://tempuri.org/IService1/GetDataUsingDataContract" message="tns:IService1_GetDataUsingDataContract_InputMessage"/>
            <wsdl:output wsaw:Action="http://tempuri.org/IService1/GetDataUsingDataContractResponse" message="tns:IService1_GetDataUsingDataContract_OutputMessage"/>
        </wsdl:operation>
    </wsdl:portType>
    <wsdl:binding name="BasicHttpBinding_IService1" type="tns:IService1">
        <soap:binding transport="http://schemas.xmlsoap.org/soap/http"/>
        <wsdl:operation name="GetData">
            <soap:operation soapAction="http://tempuri.org/IService1/GetData" style="document"/>
            <wsdl:input>
                <soap:body use="literal"/>
            </wsdl:input>
            <wsdl:output>
                <soap:body use="literal"/>
            </wsdl:output>
        </wsdl:operation>
        <wsdl:operation name="GetDataUsingDataContract">
            <soap:operation soapAction="http://tempuri.org/IService1/GetDataUsingDataContract" style="document"/>
            <wsdl:input>
                <soap:body use="literal"/>
            </wsdl:input>
            <wsdl:output>
                <soap:body use="literal"/>
            </wsdl:output>
        </wsdl:operation>
    </wsdl:binding>
    <wsdl:service name="Service1">
        <wsdl:port name="BasicHttpBinding_IService1" binding="tns:BasicHttpBinding_IService1">
            <soap:address location="http://localhost/Turien.Dynamic.Basic/Service1.svc"/>
        </wsdl:port>
    </wsdl:service>
</wsdl:definitions>

使用我的代码来提取 WSDL,我得到了这个:

<wsdl:definitions xmlns:wsx="http://schemas.xmlsoap.org/ws/2004/09/mex" xmlns:wsu="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd" xmlns:wsa10="http://www.w3.org/2005/08/addressing" xmlns:wsp="http://www.w3.org/ns/ws-policy" xmlns:wsap="http://schemas.xmlsoap.org/ws/2004/08/addressing/policy" xmlns:msc="http://schemas.microsoft.com/ws/2005/12/wsdl/contract" xmlns:soap12="http://schemas.xmlsoap.org/wsdl/soap12/" xmlns:wsa="http://schemas.xmlsoap.org/ws/2004/08/addressing" xmlns:wsam="http://www.w3.org/2007/05/addressing/metadata" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:tns="http://tempuri.org/" xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/" xmlns:wsaw="http://www.w3.org/2006/05/addressing/wsdl" xmlns:soapenc="http://schemas.xmlsoap.org/soap/encoding/" xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/" name="service" targetNamespace="http://tempuri.org/">
    <wsdl:types>
        <xsd:schema targetNamespace="http://tempuri.org/Imports">
            <xsd:import namespace="http://tempuri.org/"/>
            <xsd:import namespace="http://schemas.microsoft.com/2003/10/Serialization/"/>
            <xsd:import namespace="http://schemas.datacontract.org/2004/07/Turien.Dynamic.Basic"/>
        </xsd:schema>
    </wsdl:types>
    <wsdl:message name="IService1_GetData_InputMessage">
        <wsdl:part name="parameters" element="tns:GetData"/>
    </wsdl:message>
    <wsdl:message name="IService1_GetData_OutputMessage">
        <wsdl:part name="parameters" element="tns:GetDataResponse"/>
    </wsdl:message>
    <wsdl:message name="IService1_GetDataUsingDataContract_InputMessage">
        <wsdl:part name="parameters" element="tns:GetDataUsingDataContract"/>
    </wsdl:message>
    <wsdl:message name="IService1_GetDataUsingDataContract_OutputMessage">
        <wsdl:part name="parameters" element="tns:GetDataUsingDataContractResponse"/>
    </wsdl:message>
    <wsdl:portType name="IService1">
        <wsdl:operation name="GetData">
            <wsdl:input wsam:Action="http://tempuri.org/IService1/GetData" message="tns:IService1_GetData_InputMessage"/>
            <wsdl:output wsam:Action="http://tempuri.org/IService1/GetDataResponse" message="tns:IService1_GetData_OutputMessage"/>
        </wsdl:operation>
        <wsdl:operation name="GetDataUsingDataContract">
            <wsdl:input wsam:Action="http://tempuri.org/IService1/GetDataUsingDataContract" message="tns:IService1_GetDataUsingDataContract_InputMessage"/>
            <wsdl:output wsam:Action="http://tempuri.org/IService1/GetDataUsingDataContractResponse" message="tns:IService1_GetDataUsingDataContract_OutputMessage"/>
        </wsdl:operation>
    </wsdl:portType>
    <wsdl:binding name="BasicHttpBinding_IService1" type="tns:IService1">
        <soap:binding transport="http://schemas.xmlsoap.org/soap/http"/>
        <wsdl:operation name="GetData">
            <soap:operation soapAction="http://tempuri.org/IService1/GetData" style="document"/>
            <wsdl:input>
                <soap:body use="literal"/>
            </wsdl:input>
            <wsdl:output>
                <soap:body use="literal"/>
            </wsdl:output>
        </wsdl:operation>
        <wsdl:operation name="GetDataUsingDataContract">
            <soap:operation soapAction="http://tempuri.org/IService1/GetDataUsingDataContract" style="document"/>
            <wsdl:input>
                <soap:body use="literal"/>
            </wsdl:input>
            <wsdl:output>
                <soap:body use="literal"/>
            </wsdl:output>
        </wsdl:operation>
    </wsdl:binding>
    <wsdl:service name="service">
        <wsdl:port name="BasicHttpBinding_IService1" binding="tns:BasicHttpBinding_IService1">
            <soap:address location="http://localhost/Turien.Dynamic.Basic/Service.svc"/>
        </wsdl:port>
    </wsdl:service>
</wsdl:definitions>

如您所见,缺少属性BoolValueStringValue 的复杂类型/类StringValue

【问题讨论】:

现在我正在使用一种解决方法。这对我的 POC 来说已经足够了。下周五我会再试试你的答案。 【参考方案1】:

你需要先获取元数据集:

        var exporter2 = new WsdlExporter();
        exporter2.ExportEndpoint(myServiceEndpoints[0]);
        var metadataSet = exporter.GetGeneratedMetadata();
        var schemaStream = new MemoryStream();
        metadataSet.WriteTo(new XmlTextWriter(new StreamWriter(schemaStream)));
        schemaStream.Position = 0;
        var source = XDocument.Load(
            new XmlTextReader(schemaStream));

        Console.WriteLine(source);

拿到metadataSet后,我们需要截取它的内容,因为SOAPUI不能识别MetadataSection,我们只需要提取wsdl:definitions中间的内容。

更新

wsdl:definitions 包含端点信息:

我不确定你那边发生了什么,但在我这边是可行的。我将 source 保存到一个 txt 文件中并删除了不必要的节点。

【讨论】:

感谢您的回答。您选择的部分看起来与我开始的 38 行完全相同。所以我很抱歉,那里没有进展......:/ 我按照你的方法得到的“wsdl:definitions”真的没用,和我之前得到的有点像但又不一样。根据你的方法得到的“wsdl:definitions”没有端点信息可能是这个原因造成的,你可以尝试使用我的代码。 我试过了,没用。 SoapUI 和 WcfTestClient 报错。 我更新了我的回复。 我再次尝试了一个完全干净的新项目。我用这些信息更新了我的问题。你是对的......使用 SoapUI 加载 WDL 文件不会产生错误。但它也不会产生正确的客户端界面。我缺少(在我的示例中)具有 BoolValue 和 StringValue 属性的类 CompositeType。复杂类型完全丢失/为空。您的示例生成与我的示例相同的不完整 WSDL。

以上是关于使用 WCF WsdlExporter 如何创建与 singleWsdl 完全一样的 WSDL?的主要内容,如果未能解决你的问题,请参考以下文章

如何使用 WSDL 文件创建 WCF 代理?

如何以编程方式为 WCF 服务创建自签名证书?

如何创建更新数据库日志表的 wcf 服务或 Web 服务?

为非托管 C++ 客户端创建 WCF 服务

如何创建返回文件(.xlsx)的 WCF 服务

如何使用 makecert 创建 WCF 接受的 X509 证书