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
-- 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
You must be a registered user to add a comment. If you've already registered, sign in. Otherwise, register and sign in.