Back on the old blog, I posted about creating web services for BizTalk that accepted generic XML. In one of my current projects, a similar scenario came up. We have a WSDL that we must conform to, but wanted to accept generic content and validate AFTER the message reaches BizTalk.
Our SAP system will publish messages in real-time to BizTalk. The WSDL for the service SAP will consume is already defined. So, we built a web service for BizTalk (using the Web Services Publishing Wizard) that conforms to that WSDL. When data comes into the service, BizTalk routes it around to all interested parties. The SOAP request looks like this …
But what if the data is structurally incorrect? Because the auto-generated web service serializes the SOAP input into a strongly typed object, a request with an invalid structure never reaches the code that sends the payload to BizTalk. The serialization into the type fails, no exception is thrown (because the service call is asynchronous from SAP), and there are no exceptions in the Event Log or within BizTalk. Yikes! The only proof that the service was even called exists in the [IIS 6.0] web server logs. I can see here that a POST was made, but nowhere else can I verify that a connection was attempted.
So I don’t like that. I want an audit trail that minimally shows me that BizTalk received the message. So, if we change the service input to something more generic (while still conforming to the WSDL), we can get the message into BizTalk and then validate it. How do you make the service more generic? I took the auto-generated BizTalk web service, and modified the method (changes in bold):
{System.Collections.ArrayList inHeaders = null;
System.Collections.ArrayList inoutHeaders = null;
System.Collections.ArrayList inoutHeaderResponses = null;
System.Collections.ArrayList outHeaderResponses = null;
System.Web.Services.Protocols.SoapUnknownHeader[] unknownHeaderResponses = null;
// Parameter information
object[] invokeParams = new object[] {part};
Microsoft.BizTalk.WebServices.ServerProxy.ParamInfo[] inParamInfos =
new Microsoft.BizTalk.WebServices.ServerProxy.ParamInfo[]
{
new Microsoft.BizTalk.WebServices.ServerProxy.ParamInfo(
typeof(System.Xml.XmlElement)
, “part”)
};
Microsoft.BizTalk.WebServices.ServerProxy.ParamInfo[] outParamInfos = null;
string bodyTypeAssemblyQualifiedName = null;
// BizTalk invocation
this.Invoke(“ProcessModifySAPVendor”,
invokeParams, inParamInfos, outParamInfos, 0,
bodyTypeAssemblyQualifiedName, inHeaders,
inoutHeaders, out inoutHeaderResponses,
out outHeaderResponses, null, null, null,
out unknownHeaderResponses, true, false);
}
If you want to see more about the XmlAnyElement, check out this article. So once I modify my service this way, the SOAP request for the service now looks like this …
The service caller can execute this service operation without changing anything. The same SOAP action and operation name and namespaces still apply. We’ve only made the payload generic. Now, the next step for us was to have a custom receive pipeline that validated the content. Here, on the XmlDisassembler pipeline component, I chose to Validate document structure on the inbound message.
Now, if I send in a lousy message (bad structure, invalid data types, etc), I get the [IIS 6.0] web server log mention, but I ALSO get a suspended message within BizTalk! The message was able to be received, and only got validated after it had successfully reached the BizTalk infrastructure. Now, I have a record of the message and details about what went wrong …
Now, I wouldn’t advise this pattern in most cases. Services or components that take “any” object/content are dangerous and a bit lazy. That said, in our case, this is a service that is ONLY called by one system (SAP), and, provides us with a much-needed validation/audit capability.
Technorati Tags: BizTalk