It's a set of Kong plugins which are developed in Python and use the Python library lxml.etree binding for the GNOME C libraries libxml2 and libxslt.
The plugins handle the XML Request and the XML Response in this order:
Request:
XSLT TRANSFORMATION - BEFORE XSD
: Transform the XML request with XSLT (XSLTransformation) before XSD Validation (step #2)XSD VALIDATION
: Validate XML request against its XSD schemaXSLT TRANSFORMATION - AFTER XSD
: Transform the XML request with XSLT (XSLTransformation) after XSD Validation (step #2)ROUTING BY XPATH
: change the Route of the request to a different hostname and path depending of XPath condition
Response:
XSLT TRANSFORMATION - BEFORE XSD
: Transform the XML response before step #6XSD VALIDATION
: Validate the XML response against its XSD schemaXSLT TRANSFORMATION - AFTER XSD
: Transform the XML response after step #6
Each handling is optional, except the XSLT TRANSFORMATION - AFTER XSD
of the Response.
In case of misconfiguration the Plugin sends to the consumer an HTTP 500 Internal Server Error <soap:Fault>
(with the error detailed message)
- Build a new Docker 'Kong Gateway' image with Python dependencies and the plugin code
docker build -t kong-gateway-xml-handling .
-
Create and prepare a PostgreDB called
kong-database-xml-handling
. See documentation. -
Start the Kong Gateway
./start-kong.sh
-
Create a Kong Service named
calcWebService
with this URL: https://ecs.syr.edu/faculty/fawcett/Handouts/cse775/code/calcWebService/Calc.asmx. This simple backend Web Service adds 2 numbers. -
Create a Route on the Service
calcWebService
with thepath
value/calcWebService
-
Call the
calcWebService
through the Kong Gateway Route by using httpie tool
http POST http://localhost:8000/calcWebService \
Content-Type:"text/xml; charset=utf-8" \
--raw "<?xml version=\"1.0\" encoding=\"utf-8\"?>
<soap:Envelope xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\" xmlns:soap=\"http://schemas.xmlsoap.org/soap/envelope/\">
<soap:Body>
<Add xmlns=\"http://tempuri.org/\">
<a>5</a>
<b>7</b>
</Add>
</soap:Body>
</soap:Envelope>"
The expected result is 12
:
<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/" ...>
<soap:Body>
<AddResponse xmlns="http://tempuri.org/">
<AddResult>12</AddResult>
</AddResponse>
</soap:Body>
</soap:Envelope>
The plugin applies a XSLT Transformation on XML request before the XSD Validation.
In this example the XSLT adds the value <b>8</b>
that will be not present in the request.
Add xml-request-1-transform-xslt-before
plugin and configure the plugin with:
XsltTransform
property with this XSLT definition:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output omit-xml-declaration="yes" indent="yes"/>
<xsl:strip-space elements="*"/>
<xsl:template match="node()|@*">
<xsl:copy>
<xsl:apply-templates select="node()|@*"/>
</xsl:copy>
</xsl:template>
<xsl:template match="//*[local-name()='a']">
<xsl:copy-of select="."/>
<b>8</b>
</xsl:template>
</xsl:stylesheet>
Use request defined at step #3, remove <b>7</b>
, the expected result is no longer 12
but 13
:
<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/" ...>
<soap:Body>
<AddResponse xmlns="http://tempuri.org/">
<AddResult>13</AddResult>
</AddResponse>
</soap:Body>
</soap:Envelope>
Example #2: Request | XSD VALIDATION
: calling incorrectly calcWebService
and detecting issue in the Request with XSD schema
Calling incorrectly calcWebService
and detecting issue in the Request with XSD schema.
We call incorrectly the Service by injecting a SOAP error; the plugin detects it, sends an error message to the Consumer and Kong doesn't call the SOAP backend API.
Add xml-request-2-validate-xsd
plugin and configure the plugin with:
XsdApiSchema
property with this value:
<xs:schema attributeFormDefault="unqualified" elementFormDefault="qualified" targetNamespace="http://tempuri.org/" xmlns:xs="http://www.w3.org/2001/XMLSchema">
<xs:element name="Add" type="tem:AddType" xmlns:tem="http://tempuri.org/"/>
<xs:complexType name="AddType">
<xs:sequence>
<xs:element type="xs:integer" name="a" minOccurs="1"/>
<xs:element type="xs:integer" name="b" minOccurs="1"/>
</xs:sequence>
</xs:complexType>
<xs:element name="Subtract" type="tem:SubtractType" xmlns:tem="http://tempuri.org/"/>
<xs:complexType name="SubtractType">
<xs:sequence>
<xs:element type="xs:integer" name="a" minOccurs="1"/>
<xs:element type="xs:integer" name="b" minOccurs="1"/>
</xs:sequence>
</xs:complexType>
</xs:schema>
Use request defined at step #3, change <soap:Envelope>
by <soap:EnvelopeKong>
and </soap:Envelope>
by </soap:EnvelopeKong>
=> Kong says:
<faultstring>XSD validation failed: Element '{http://schemas.xmlsoap.org/soap/envelope/}EnvelopeKong': No matching global declaration available for the validation root. (<string>, line 0)</faultstring>
Use request defined at step #3, remove <a>5</a>
=> there is an error because the <a>
tag has the minOccurs="1"
XSD property and Kong says:
<faultstring>XSD validation failed: Element '{http://tempuri.org/}b': This element is not expected. Expected is ( {http://tempuri.org/}a ). (<string>, line 0)</faultstring>
The plugin applies a XSLT Transformation on XML request after the XSD Validation.
In this example we change the Tag name from <Subtract>...</Subtract>
(present in the request) to <Add>...</Add>
.
Add xml-request-3-transform-xslt-after
plugin and configure the plugin with:
XsltTransform
property with no value
Without XSLT: Use request defined at step #3, rename the Tag <Add>...</Add>
, to <Subtract>...</Subtract>
the expected result is -3
<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/" ...>
<soap:Body>
<SubtractResponse xmlns="http://tempuri.org/">
<SubtractResult>-3</SubtractResult>
</SubtractResponse>
</soap:Body>
</soap:Envelope>
Configure xml-request-3-transform-xslt-after
plugin with:
XsltTransform
property with this XSLT definition:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output omit-xml-declaration="yes" indent="yes"/>
<xsl:strip-space elements="*"/>
<xsl:template match="node()|@*">
<xsl:copy>
<xsl:apply-templates select="node()|@*"/>
</xsl:copy>
</xsl:template>
<xsl:template match="//*[local-name()='Subtract']">
<Add xmlns="http://tempuri.org/"><xsl:apply-templates select="@*|node()" /></Add>
</xsl:template>
</xsl:stylesheet>
With XSLT: Use request defined at step #3, rename the Tag <Add>...</Add>
, to <Subtract>...</Subtract>
the expected result is 13
:
<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/" ...>
<soap:Body>
<AddResponse xmlns="http://tempuri.org/">
<AddResult>13</AddResult>
</AddResponse>
</soap:Body>
</soap:Envelope>
Example #4: Request | ROUTING BY XPATH
: change the Route of the request to a different hostname and path depending of XPath condition
The plugin searches the XPath entry and compares it to a Condition value. If this is the right Condition value, the plugin changes the host and the path of the Route.
This example uses a new backend Web Service (https://websrv.cs.fsu.edu/~engelen/calc.wsdl) which provides the same capabilities as calcWebService
Service (https://ecs.syr.edu) defined at step #1.
Note: the websrv.cs.fsu.edu
introduces a new XML NameSpace so we have to change the XSLT transform to make the proper call.
Add a Kong Upstream
named websrv.cs.fsu.edu
and defines a target
with websrv.cs.fsu.edu:443
value.
Add xml-request-4-route-by-xpath
plugin and configure the plugin with:
RouteToPath
property with the value/~engelen/calcserver.cgi
RouteToUpstream
property with the valuewebsrv.cs.fsu.edu
XPath
property with the value.//{http://tempuri.org/}a
XPathCondition
property with the value5
Open xml-request-3-transform-xslt-after
plugin and configure the plugin with:
XsltTransform
property with this XSLT definition:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output omit-xml-declaration="yes" indent="yes"/>
<xsl:strip-space elements="*"/>
<xsl:template match="node()|@*">
<xsl:copy>
<xsl:apply-templates select="node()|@*"/>
</xsl:copy>
</xsl:template>
<xsl:template match="//*[local-name()='Subtract']">
<urn:add xmlns:urn="urn:calc"><xsl:apply-templates select="@*|node()" /></urn:add>
</xsl:template>
</xsl:stylesheet>
Use request defined at step #3, rename the Tag <Add>...</Add>
, to <Subtract>...</Subtract>
. The expected result is 13
. The new Route (to websrv.cs.fsu.edu
) sends a slightly different response:
- SOAP tag are in capital letter:
<SOAP-ENV:Envelope>
instead of<soap:Envelope>
- Namespace is injected:
xmlns:ns="urn:calc"
<SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/" ... xmlns:ns="urn:calc">
<SOAP-ENV:Body SOAP-ENV:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/">
<ns:addResponse>
<result>13</result>
</ns:addResponse>
</SOAP-ENV:Body>
</SOAP-ENV:Envelope>
Example #5: Response | XSLT TRANSFORMATION - BEFORE XSD
: changing a Tag name in XML response by using XSLT
The plugin applies a XSLT Transformation on XML response before the XSD Validation. In this example the XSLT changes the Tag names:
- from
<ns:addResponse>...</ns:addResponse>
(present in the response) to<addResponse>...</addResponse>
- from
<result>...</result>
(present in the response) to<KongResult>...</KongResult>
Configure plugins:
- Add
xml-response-3-transform-xslt-after
plugin with default parameters - Add
xml-response-2-validate-xsd
plugin with default parameters - Add
xml-response-1-transform-xslt-before
plugin and configure the plugin with:XsltTransform
property with this XSLT definition:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="@*|node()">
<xsl:copy>
<xsl:apply-templates select="@*|node()" />
</xsl:copy>
</xsl:template>
<xsl:template match="//*[local-name()='addResponse']">
<addResponse>
<xsl:apply-templates select="@*|node()" />
</addResponse>
</xsl:template>
<xsl:template match="//*[local-name()='result']">
<KongResult><xsl:apply-templates select="@*|node()" /></KongResult>
</xsl:template>
</xsl:stylesheet>
Use request defined at step #3, rename the Tag <Add>...</Add>
, to <Subtract>...</Subtract>
the expected result is <KongResult>13</KongResult>
:
<SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/" ... xmlns:ns="urn:calc">
<SOAP-ENV:Body SOAP-ENV:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/">
<addResponse>
<KongResult>13</KongResult>
</addResponse>
</SOAP-ENV:Body>
</SOAP-ENV:Envelope>
Configure xml-response-2-validate-xsd
plugin with:
XsdApiSchema
property with this value:
<xs:schema attributeFormDefault="unqualified" elementFormDefault="qualified" xmlns:xs="http://www.w3.org/2001/XMLSchema">
<xs:element name="addResponse" type="addResponseType"/>
<xs:complexType name="addResponseType">
<xs:sequence>
<xs:element type="xs:string" name="KongResult"/>
</xs:sequence>
</xs:complexType>
</xs:schema>
Example #7: Response | XSLT TRANSFORMATION - AFTER XSD
: transforming the SOAP response to a XML response
In this example the XSLT removes all tags and converts the response from SOAP to XML.
Configure xml-response-3-transform-xslt-after
plugin with:
XsltTransform
property with this value:
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/"
exclude-result-prefixes="soapenv">
<xsl:strip-space elements="*"/>
<xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/>
<!-- remove all elements in the soapenv namespace -->
<xsl:template match="soapenv:*">
<xsl:apply-templates select="node()"/>
</xsl:template>
<!-- for the remaining elements (i.e. elements in the default namespace) ... -->
<xsl:template match="*">
<!-- ... create a new element with similar name in no-namespace -->
<xsl:element name="{local-name()}">
<xsl:apply-templates select="@*|node()"/>
</xsl:element>
</xsl:template>
<!-- convert attributes to elements -->
<xsl:template match="@*">
<xsl:element name="{local-name()}">
<xsl:value-of select="." />
</xsl:element>
</xsl:template>
</xsl:stylesheet>
Use request defined at step #3, rename the Tag <Add>...</Add>
, to <Subtract>...</Subtract>
the expected result is:
<?xml version="1.0" encoding="utf-8"?>
<addResponse>
<KongResult>13</KongResult>
</addResponse>