So I spent a little time thinking about generic web service on ramps after reading Peter Kelcey’s post on the Microsoft ESB Guidance. The shockingly observant of you may recall that I wrote a post a few months back on how to build a BizTalk web service that accepted generic input as well.
I’m just not completely sold on the “one ramp to the bus” concept yet. Primarily because I worry about loosely typed service/object input. It’s nice that I can use the same snippet of code to publish a message to a service regardless of the message I’m using, but, ensuring that you’re passing the *right* data is arguably more important than the convenience of only having a few web services in the infrastructure. Now that we have such mature, powerful web service management offerings from companies like SOA Software, the physical number of services seems less of a concern.
All that said, I was having dinner with some techie buddies the other night and I raised this issue. We agreed that being able to validate your input PRIOR to sending the message to the generic service would be a way to mitigate this concern. So, I thought I’d try and build a simple “validation service” with BizTalk using orchestration and pipelines.
I started with a very basic couple of schemas. What I want to do is provide a simple service that take in any XML input, and returns back any validation errors.
Next comes the pipeline itself. In my Disassemble stage, I’m using the XML Disassembler with each available schema defined in the Document Schemas property. I set Validate Document to false here, and then used an XML Validator pipeline component later on to do the actual schema validation.
To start with, this is all I need to test. I wanted to test 4 different scenarios:
- Invalid data types (e.g. pass in string when int is expected)
- Missing nodes
- Adding additional records even though maxOccurs equals 1
- Passing in a message where no corresponding schema exists
I think that covers the basics of validation for now. So I built a pure messaging solution (FILE receive location, with FILE send port) and applied my custom receive pipeline to the inbound receive location. As expected, the result of each of the above scenarios was a suspended message. So my pipeline works. Now, I wanted to call this pipeline from an orchestration so that I would have a synchronous service that gracefully captured exceptions.
So, in my orchestration I referenced Microsoft.XLANGS.Pipelines.dll and Microsoft.BizTalk.Pipeline.dll. The process receives a document in (of type XmlDocument), and then within an atomic transaction (contained within a larger long-running transaction that catches exceptions), I called into my pipeline component. This is new functionality in BizTalk Server 2006. The code in my Expression Shape is:
RcvPipeOutMsgs = Microsoft.XLANGs.Pipeline.XLANGPipelineManager.ExecuteReceivePipeline(typeof(Microsoft.Demo.Blog.ValidationSvc.Rcv_ValidateOnRampMsg), OnRampInput);
The RcvPipeOutMsgs is a variable of type Microsoft.XLANGS.Pipeline.ReceivePipelineOutputMessages, you see that I use the typeof my pipeline component, and the OnRampInput is the inbound message of type XmlDocument. The whole orchestration looks like this:
You can see that I catch any exceptions, set status variables, and then construct a “response” message to the service caller. I then walked through the Web Services Publishing Wizard (after changing my operation name from Operation_1 to PostDocToValidate) and built a web service that takes in any XML, and returns a common “response” message.
I then built a simple WinForm to call the service, passing in a variety of inputs. If I pass in a valid message, I get a “success” message. If I pass in a message with invalid content (scenarios 1-3 above), I get an error:
Looks good. HOWEVER, if I pass in a garbage message (that is, no schema exists), I get this:
I’m calling the exact same pipeline that I did in my “pure messaging” solution which DID raise an error for garbage messages, but when calling this pipeline from an orchestration, the pipeline isn’t raising an exception. I have tried about 37 different combinations of pipeline components, property settings, message types (tried switching from XML input to string) and I can’t see to get an error to occur.
So, there you have a 3/4 validation service. However, without properly handling bad messages, it’s not entirely useful. Thoughts? I really wanted to use the standard BizTalk components to validate (e.g. out of the box pipeline components), instead of using any sort of cool code-based solution. Anything I might be missing here?