Welcome to MSDN Blogs Sign in | Join | Help

SQL Protocols

Topics from the Microsoft SQL Server Protocols team - Netlibs, TDS, SQL Browser, etc...
Building T-SQL Custom WSDL generator

We've gotten feedback from customers who have asked if it is possible to write a custom WSDL generator in T-SQL.  For those customers who do not wish to enable SQL CLR support, the following sample T-SQL SP can be used as a starting point for generating your own custom WSDL.  Please be advised that this sample is just as that, a sample.  It is NOT production level code and is provided as a technical demonstration that it is possible.

NOTE: The server response format for a SP can not be changed, unless you specify FORMAT = NONE on the ENDPOINT WEBMETHOD keyword syntax.  At which point, you are responsible to control the exact response.

This post will discuss just the WSDL portion.  A seperate posting later on will discuss how to customize SP response formats.  Please note that this post contains the entire T-SQL SP code and as such is a long posting.  The sample code is below:

CREATE PROCEDURE SpHttpGenerateWsdl
    @EndpointID int,
    @IsSSL bit,
    @Host nvarchar(128),
    @QueryString nvarchar(128),
    @UserAgent nvarchar(128)
as
begin
  set nocount on
  declare @http int
  set @http = 1
  declare @soap int
  set @soap = 1
  declare @started int
  set @started = 0
  declare @outputWSDL nvarchar(max)
 
  -- define the set of preset strings needed in the WSDL document
  set @outputWSDL = N'<wsdl:definitions xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/" xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/"'
  declare @wsdlStartTypes nvarchar(50)
  set @wsdlStartTypes = N'<wsdl:types>'
  declare @wsdlEndTypes nvarchar(20)
  set @wsdlEndTypes = N'</wsdl:types>'
  declare @xsdStartSchema nvarchar(150)
  set @xsdStartSchema = N'<xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema" attributeFormDefault="qualified" elementFormDefault="qualified" targetNamespace='
  declare @xsdEndSchema nvarchar(20)
  set @xsdEndSchema = N'</xsd:schema>'
  declare @xsdStartElement nvarchar (50)
  set @xsdStartElement = N'<xsd:element name='
  declare @xsdEndElement nvarchar(20)
  set @xsdEndElement = N'</xsd:element>'
  declare @xsdStartComplexType nvarchar(50)
  set @xsdStartComplexType = N'<xsd:complexType><xsd:sequence>'
  declare @xsdEndComplexType nvarchar(50)
  set @xsdEndComplexType = N'</xsd:sequence></xsd:complexType>'
  declare @wsdlStartMessage nvarchar(100)
  set @wsdlStartMessage = N'<wsdl:message name='
  declare @wsdlEndMessage nvarchar(20)
  set @wsdlEndMessage = N'</wsdl:message>'
  declare @wsdlStartPart nvarchar(50)
  set @wsdlStartPart = N'<wsdl:part name="parameters" element='
  declare @wsdlEndPart nvarchar(20)
  set @wsdlEndPart = N'</wsdl:part>'
  declare @wsdlStartPortType nvarchar(50)
  set @wsdlStartPortType = N'<wsdl:portType name='
  declare @wsdlEndPortType nvarchar(20)
  set @wsdlEndPortType = N'</wsdl:portType>'
  declare @wsdlStartOperation nvarchar(50)
  set @wsdlStartOperation = N'<wsdl:operation name='
  declare @wsdlEndOperation nvarchar(20)
  set @wsdlEndOperation = N'</wsdl:operation>'
  declare @wsdlStartInput nvarchar(50)
  set @wsdlStartInput = N'<wsdl:input name='
  declare @wsdlEndInput nvarchar(20)
  set @wsdlEndInput = N'</wsdl:input>'
  declare @wsdlStartOutput nvarchar(50)
  set @wsdlStartOutput = N'<wsdl:output name='
  declare @wsdlEndOutput nvarchar(20)
  set @wsdlEndOutput = N'</wsdl:output>'
  declare @wsdlStartBinding nvarchar(50)
  set @wsdlStartBinding = N'<wsdl:binding name='
  declare @wsdlEndBinding nvarchar(20)
  set @wsdlEndBinding = N'</wsdl:binding>'
  declare @soapBinding nvarchar(100)
  set @soapBinding = N'<soap:binding transport="http://schemas.xmlsoap.org/soap/http" style="document"/>'
  declare @soapStartOperation nvarchar(50)
  set @soapStartOperation = N'<soap:operation soapAction='
  declare @soapEndOperation nvarchar(20)
  set @soapEndOperation = N' style="document" />'
  declare @soapBody nvarchar(50)
  set @soapBody = N'<soap:body use="literal" />'
  declare @wsdlStartService nvarchar(50)
  set @wsdlStartService = N'<wsdl:service name='
  declare @wsdlEndService nvarchar(20)
  set @wsdlEndService = N'</wsdl:service>'
  declare @wsdlStartPort nvarchar(50)
  set @wsdlStartPort = N'<wsdl:port name='
  declare @wsdlEndPort nvarchar(20)
  set @wsdlEndPort = N'</wsdl:port>'
  declare @soapStartAddress nvarchar(50)
  set @soapStartAddress = N'<soap:address location='
  declare @soapEndAddress nvarchar(20)
  set @soapEndAddress = N'</soap:address>'
  declare @wsdlEndDefinitions nvarchar(20)
  set @wsdlEndDefinitions = N'</wsdl:definitions>'
 
  -- some local variables
  declare @endpointWsdl nvarchar(100)
  declare @endpointProtocol int
  declare @endpointType int
  declare @endpointState int
  declare @endpointBatches bit
  declare @endpointMethodCount int

  -- make sure WSDL is enabled on the endpoint
  select @endpointWsdl = wsdl_generator_procedure from sys.soap_endpoints where endpoint_id = @EndpointID
  if (NOT (LEN(@endpointWsdl) > 0))
  begin
    raiserror ('WSDL generation is disabled for this endpoint.', 16, 1)
  end

  -- make sure the query string is requesting for WSDL
  if (N'WSDL' <> UPPER(@QueryString))
  begin
    raiserror ('Unsupported Action, please double check value of query string.', 16, 1)
  end

  -- make sure the endpoint actually exists
  if ((select endpoint_id from sys.endpoints where endpoint_id = @EndpointID) is NULL)
  begin
 raiserror ('Specified Endpoint is invalid.', 16, 1)
  end

  -- make sure the endpoint is a SOAP endpoint and is started
  select @endpointProtocol=protocol,
   @endpointType=type,
    @endpointState=state
   from sys.endpoints where endpoint_id = @EndpointID

 

  if ((@endpointProtocol <> @http) OR (@endpointType <> @soap) OR (@endpointState <> @started))
  begin
    RAISERROR('Specified Endpoint is not a SOAP endpoint or is not started', 16, 1)
  end

  -- query to see if SqlBatch is enabled on the endpoint
  select @endpointBatches = is_sql_language_enabled from sys.soap_endpoints where endpoint_id = @EndpointID

  -- check the number of web methods specified on the endpoint
  select @endpointMethodCount = count(*) from sys.endpoint_webmethods where endpoint_id = @EndpointID

  -- if SqlBatch is enabled or if there is at least one web method, then generate WSDL
  if ((@endpointBatches = 1) OR (@endpointMethodCount > 0))
  begin
    -- Note: this sample does not actually general the definition for the SqlBatch method
    -- create a temp table to store the list of webmethods on the endpoint
    create table #tempWSDLMethod (db nvarchar(20) NOT NULL,
                                  oOwner nvarchar(20) NOT NULL,
                                  oName nvarchar(50) NOT NULL)

    insert #tempWSDLMethod (db, oOwner, oName)
     select LEFT(object_name, CHARINDEX(N'.', object_name)-1),
             SUBSTRING(object_name, CHARINDEX(N'.', object_name)+1, CHARINDEX(N'.', object_name, CHARINDEX(N'.', object_name)+1)-CHARINDEX(N'.', object_name)-1),
             RIGHT(object_name, LEN(object_name)-CHARINDEX(N'.', object_name, CHARINDEX(N'.', object_name)+1))
     from sys.endpoint_webmethods where endpoint_id = @EndpointID

    -- create a temp table to keep track of all the information needed to generate WSDL
    create table #tempWSDLTable (id int identity primary key,
                                 webMethodNamespace nvarchar(max) NOT NULL,
                                 webMethodName nvarchar(max) NOT NULL,
                                 paramName nvarchar(100) NOT NULL,
                                 namespaceSuffix int DEFAULT 1)

    -- insert appropriate info to temp table
    declare webMethodDb_Cursor CURSOR FOR
  SELECT distinct db from #tempWSDLMethod

 declare @tDbName nvarchar(50)
 open webMethodDb_Cursor
 FETCH NEXT FROM webMethodDb_Cursor INTO @tDbName
 WHILE @@FETCH_STATUS = 0
 BEGIN
        declare @tQuery nvarchar(max)
        set @tQuery = N'use ' + @tDbName + '; insert #tempWSDLTable (webMethodNamespace, webMethodName, paramName)
            select a.namespace, a.method_alias, RIGHT(b.name, (LEN(b.name)-1))
            from sys.endpoint_webmethods as a, sys.parameters as b
            where a.endpoint_id = ' + CAST(@EndpointID as nvarchar(10))
            + N' and b.object_id = object_id(a.object_name) and (LEN(b.name) > 0)'
  exec (@tQuery)
  FETCH NEXT FROM webMethodDb_Cursor INTO @tDbName
 END

 CLOSE webMethodDb_Cursor
 DEALLOCATE webMethodDb_Cursor

    -- generate the WSDL document
    select @outputWSDL = @outputWSDL + N' xmlns:tns="' + default_namespace +
          '" targetNamespace="' + default_namespace + '"'
          from sys.soap_endpoints where endpoint_id = @EndpointID

    declare webMethodNS_Cursor SCROLL CURSOR FOR
  SELECT distinct webMethodNamespace from #tempWSDLTable

 declare @tCount int
    declare @tMethodNS varchar(50)
    set @tCount = 1
 open webMethodNS_Cursor
 FETCH NEXT FROM webMethodNS_Cursor INTO @tMethodNS
 WHILE @@FETCH_STATUS = 0
 BEGIN
        select @outputWSDL = @outputWSDL + N' xmlns:s' + CAST(@tCount as nvarchar(3)) + N'="' + @tMethodNS + N'"'
        update #tempWSDLTable set namespaceSuffix = @tCount where webMethodNamespace = @tMethodNS
        set @tCount = @tCount + 1
   FETCH NEXT FROM webMethodNS_Cursor INTO @tMethodNS
 END

    select @outputWSDL = @outputWSDL + N'>'

    -- start the <wsdl:types> node
    select @outputWSDL = @outputWSDL + @wsdlStartTypes
    -- add any xsd:schema as necessary here
    -- one possibility is to store these XML schemas in a table and query the appropriate ones here
    -- and add them to the WSDL

    -- loop through the set of webmethod namespaces to add the appropriate xsd schema definitions
    FETCH FIRST FROM webMethodNS_Cursor INTO @tMethodNS
 WHILE @@FETCH_STATUS = 0
 BEGIN
        select @outputWSDL = @outputWSDL + @xsdStartSchema + N'"' + @tMethodNS + N'">'

        declare webMethodInfo_Cursor CURSOR FOR
       SELECT DISTINCT webMethodName, paramName
            from #tempWSDLTable
            where webMethodNamespace = @tMethodNS
            order by webMethodName ASC

        declare @tMethodName nvarchar(50)
        declare @tMethodNameBak nvarchar(50)
        declare @tMethodParamName nvarchar(20)
        declare @bFirstTime bit
        set @tMethodNameBak = N''
        set @bFirstTime = 1
     open webMethodInfo_Cursor
        FETCH NEXT FROM webMethodInfo_Cursor INTO @tMethodName, @tMethodParamName
      WHILE @@FETCH_STATUS = 0
     BEGIN
           if ((NOT (@bFirstTime = 1) AND (@tMethodNameBak <> @tMethodName)))
     begin
   -- close out the method name node
               select @outputWSDL = @outputWSDL + @xsdEndComplexType
               select @outputWSDL = @outputWSDL + @xsdEndElement

   -- response message structure
               select @outputWSDL = @outputWSDL + @xsdStartElement + N'"' + @tMethodNameBak + N'Response">'
               select @outputWSDL = @outputWSDL + @xsdStartComplexType
               select @outputWSDL = @outputWSDL + @xsdStartElement + N'"' + @tMethodNameBak + N'Result">'
               select @outputWSDL = @outputWSDL + @xsdEndElement
               select @outputWSDL = @outputWSDL + @xsdEndComplexType
               select @outputWSDL = @outputWSDL + @xsdEndElement
           end

    -- request message structure
           if (@tMethodNameBak <> @tMethodName)
     begin
             -- add the method name node
              select @outputWSDL = @outputWSDL + @xsdStartElement + N'"' + @tMethodName + N'">'
              select @outputWSDL = @outputWSDL + @xsdStartComplexType
           end
          
           -- add the parameters
           -- Make sure the appropriate parameter type is specified here
           -- This sample leaves it as xsd:anyType which is normally handled as an Object
           select @outputWSDL = @outputWSDL + @xsdStartElement + N'"' + @tMethodParamName + N'">'
           select @outputWSDL = @outputWSDL + @xsdEndElement

     set @bFirstTime = 0
           set @tMethodNameBak = @tMethodName
         FETCH NEXT FROM webMethodInfo_Cursor INTO @tMethodName, @tMethodParamName
        END

  -- close out the method name node
        select @outputWSDL = @outputWSDL + @xsdEndComplexType
        select @outputWSDL = @outputWSDL + @xsdEndElement

  -- response message structure
        select @outputWSDL = @outputWSDL + @xsdStartElement + N'"' + @tMethodNameBak + N'Response">'
        select @outputWSDL = @outputWSDL + @xsdStartComplexType
        select @outputWSDL = @outputWSDL + @xsdStartElement + N'"' + @tMethodNameBak + N'Result">'
        select @outputWSDL = @outputWSDL + @xsdEndElement
        select @outputWSDL = @outputWSDL + @xsdEndComplexType
        select @outputWSDL = @outputWSDL + @xsdEndElement

        CLOSE webMethodInfo_Cursor
     DEALLOCATE webMethodInfo_Cursor

        select @outputWSDL = @outputWSDL + @xsdEndSchema

  FETCH NEXT FROM webMethodNS_Cursor INTO @tMethodNS
 END
       
 CLOSE webMethodNS_Cursor
  DEALLOCATE webMethodNS_Cursor
   
    -- close the <wsdl:types> node
    select @outputWSDL = @outputWSDL + @wsdlEndTypes

    -- need to loop through each webmethod on the endpoint
    declare webMethodInfo_Cursor SCROLL CURSOR FOR
        SELECT DISTINCT webMethodName, webMethodNamespace, namespaceSuffix
        from #tempWSDLTable
        ORDER BY webMethodNamespace ASC, webMethodName ASC

    declare @tIdSuffix int
   
    open webMethodInfo_Cursor
    FETCH FIRST FROM webMethodInfo_Cursor INTO @tMethodName, @tMethodNS, @tIdSuffix
    WHILE @@FETCH_STATUS = 0
    BEGIN
        -- the <wsdl:message> node
        select @outputWSDL = @outputWSDL + @wsdlStartMessage + N'"s' + CAST(@tIdSuffix as nvarchar(3)) + N'Msg' + @tMethodName + N'SoapIn">'
        select @outputWSDL = @outputWSDL + @wsdlStartPart + N'"s' + CAST(@tIdSuffix as nvarchar(3)) + N':' + @tMethodName + N'" />'
        select @outputWSDL = @outputWSDL + @wsdlEndMessage
        select @outputWSDL = @outputWSDL + @wsdlStartMessage + N'"s' + CAST(@tIdSuffix as nvarchar(3)) + N'Msg' + @tMethodName + N'SoapOut">'
        select @outputWSDL = @outputWSDL + @wsdlStartPart + N'"s' + CAST(@tIdSuffix as nvarchar(3)) + N':' + @tMethodName + N'Response" />'
        select @outputWSDL = @outputWSDL + @wsdlEndMessage
        FETCH Next FROM webMethodInfo_Cursor INTO @tMethodName, @tMethodNS, @tIdSuffix
    END

    -- the <wsdl:portType> node
    select @outputWSDL = @outputWSDL + @wsdlStartPortType + N'"' + name + N'Soap">'
        from sys.http_endpoints where endpoint_id = @EndpointId

    FETCH FIRST FROM webMethodInfo_Cursor INTO @tMethodName, @tMethodNS, @tIdSuffix
    WHILE @@FETCH_STATUS = 0
    BEGIN
        -- the <wsdl:Operation> node
        select @outputWSDL = @outputWSDL + @wsdlStartOperation + N'"' + @tMethodName + N'">'
        select @outputWSDL = @outputWSDL + @wsdlStartInput + N'"s' + CAST(@tIdSuffix as nvarchar(3)) + N'Msg' + @tMethodName +
                             N'SoapIn" message="tns:s' + CAST(@tIdSuffix as nvarchar(3)) + N'Msg' + @tMethodName + N'SoapIn" />'
        select @outputWSDL = @outputWSDL + @wsdlStartOutput + N'"s' + CAST(@tIdSuffix as nvarchar(3)) + N'Msg' + @tMethodName +
                             N'SoapOut" message="tns:s' + CAST(@tIdSuffix as nvarchar(3)) + N'Msg' + @tMethodName + N'SoapOut" />'
        select @outputWSDL = @outputWSDL + @wsdlEndOperation
        FETCH NEXT FROM webMethodInfo_Cursor INTO @tMethodName, @tMethodNS, @tIdSuffix
    END

    select @outputWSDL = @outputWSDL + @wsdlEndPortType

    -- the <wsdl:binding> node
    select @outputWSDL = @outputWSDL + @wsdlStartBinding + N'"' + name + N'Soap" type="tns:' + name + N'Soap">'
        from sys.http_endpoints where endpoint_id = @EndpointID

    select @outputWSDL = @outputWSDL + @soapBinding

    FETCH FIRST FROM webMethodInfo_Cursor INTO @tMethodName, @tMethodNS, @tIdSuffix
    WHILE @@FETCH_STATUS = 0
    BEGIN
        select @outputWSDL = @outputWSDL + @wsdlStartOperation + N'"' + @tMethodName + N'">'
        select @outputWSDL = @outputWSDL + @soapStartOperation + N'"' + @tMethodNS + @tMethodName + N'"' + @soapEndOperation
        select @outputWSDL = @outputWSDL + @wsdlStartInput + N'"s' + CAST(@tIdSuffix as nvarchar(3)) + N'Msg' + @tMethodName + N'SoapIn">'
        select @outputWSDL = @outputWSDL + @soapBody + @wsdlEndInput
        select @outputWSDL = @outputWSDL + @wsdlStartOutput + N'"s' + CAST(@tIdSuffix as nvarchar(3)) + N'Msg' + @tMethodName + N'SoapOut">'
        select @outputWSDL = @outputWSDL + @soapBody + @wsdlEndOutput + @wsdlEndOperation
        FETCH NEXT FROM webMethodInfo_Cursor INTO @tMethodName, @tMethodNS, @tIdSuffix
    END

  CLOSE webMethodInfo_Cursor
 DEALLOCATE webMethodInfo_Cursor

    select @outputWSDL = @outputWSDL + @wsdlEndBinding

    select @outputWSDL = @outputWSDL + @wsdlStartService + N'"' + name + N'">'
        from sys.http_endpoints where endpoint_id = @EndpointID

    select @outputWSDL = @outputWSDL + @wsdlStartPort + N'"' + name + N'" binding="tns:' + name + N'Soap">'
        from sys.http_endpoints where endpoint_id = @EndpointID

    if (@IsSSL = 1)
    begin
        select @outputWSDL = @outputWSDL + @soapStartAddress + N'"https://' + @Host + N'/' + url_path + N'" />'
            from sys.http_endpoints where endpoint_id = @EndpointID
    end
    else
    begin
        select @outputWSDL = @outputWSDL + @soapStartAddress + N'"http://' + @Host + N'/' + url_path + N'" />'
            from sys.http_endpoints where endpoint_id = @EndpointID
    end   

    select @outputWSDL = @outputWSDL + @wsdlEndPort + @wsdlEndService + @wsdlEndDefinitions
  end

  -- The WSDL document must be returned to the client using this GUID as the column name.
  select @outputWSDL as N'XML_F52E2B61-18A1-11d1-B105-00805F49916B'
end
go

Jimmy Wu, SQL Server Protocols
Disclaimer: This posting is provided “AS IS” with no warranties, and confers no rights

Posted: Tuesday, November 07, 2006 11:21 PM by SQL Protocols

Comments

SQL Protocols said:

Commonly for Web Services, the exposed interfaces (webmethods) must conform to some pre-defined contract.

# February 25, 2007 5:53 PM
Leave a Comment

(required) 

(required) 

(optional)

(required) 

  
Enter Code Here: Required

Comment Notification

If you would like to receive an email when updates are made to this post, please register here

Subscribe to this post's comments using RSS

Page view tracker