BizTalk Server: REST Services Error Handling
Introduction
There are quite a few posts on different blogs that explain how to consume and expose REST services from BizTalk that are really good (this one from Mikael Håkansson about the bLogical REST Start Kit for BizTalk, or this series from Steef-Jan Wiggers about the new REST features in BizTalk 2013), but I thought that there has always been a missing piece in the picture: How to handle errors when BizTalk acts as a consumer of REST services.
Some Background
When we consume a REST service, likewise when we access any web resource in a browser of choice, there are chances that we will face scenarios where we won’t be receiving the happy “OK” HTTP result (HTTP Status code 200), but some other Status Code letting us know that something went wrong. It could be either an specific error scenario defined in the API (e.g. an “Bad Request” HTTP Status code 400 caused because of a misuse of the API), the actual REST resource not existing on the target service (HTTP Status code 404, Not Found) or even a full melt-down of the service caused by an unhandled exception (most probably an HTTP status code 500).
It all depends on the specific design and implementation of the REST API, as there are many different “ideological movements” on how some HTTP codes should be used in a REST API. I won´t get into that swampy territory, and we will just accept the fact that at some point we can face those non-200 status codes :D
The main issue with BizTalk, that looks like is still the same with BizTalk 2013 and its new WCF-WebHttp adapter is that those non-200 HTTP codes are not available on the response message to a REST call when they happen. We might want to make our solution to be able to react to those scenarios and take the corresponding actions (e.g. creating a fault message related to that failure so the operations team can be aware of it and so correct the issue).
In this post, we will take a look into how we can remediate this for both BizTalk 2010 (when using the REST Starter Kit approach to call a REST service) and BizTalk 2013 (using the WCF-WebHttp adapter).
REST Start Kit for BizTalk Server 2010 scenario
The first step we will take will be to download and install the bLogical REST Starter Kit for BizTalk, which can be downloaded from CodePlex http://biztalkrest.codeplex.com/. We will use it as a base to consume REST services from BizTalk 2010.
Once it´s all setup, we will modify the sample REST service provided with it for testing. We will just simply return a 400 HTTP Status code as a result if the inbound request ID is different from “123”.
With this we will be able to force that 400 result scenario by just modifying the inbound test message provided with the samples:
If we drop that modified message to the inbound file drop, the web service will return the 400 Status Code as a result, and both the Send port and Orchestration service instances get suspended:
OK, so that result could be expected from a WCF point of view. A ProtocolException is received by the consumer (BizTalk in our case), and the inner exception is a WebException that specifies the Status Code received (400). This should be easy then…
We are so happy with our finding that we go straight into the sample ConsumeGET.odx orchestration included in the REST Start Kit for BizTalk and add some exception handling around the interaction with the REST service.
We just add the corresponding exception handling scope and few variables to get the inner WebException and the Status code received from the service.
Build, deploy, test…. And fail :(
We see that the Orchestration is suspended again with the following error:
And that’s the key of it. The WCF adapter just gets the information form the protocol exception received and puts it into an XlangSoapException, so we cannot correctly access the information we want. At least not in an appropriate way (I’m not going to start mangling around with the XlangSoapException message text to get the HTTP Status code from there :D).
What we need to do here is to get a hook into the exception right before BizTalk jumps in and transforms it into an XlangSoapException.
Implementing the solution
What we will do for this is create a custom binding element, that in turn will create the corresponding WCF channel to handle the interaction with the service, so once the ProtocolException comes, we can play with it. Might sound a bit complex, but as you will see is simpler that it looks.
All the code for that is included in this sample in MSDN.
Basically, we have four classes involved:
- WebRequestInterceptorBindingElement: This is the binding element that we will use within a customBinding in our WCF-Custom send port configuration. It will be responsible of creating the channel factory that in turn will build the channel that will handle the exceptions appropriately before handing over to BizTalk. It inherits from BindingElement. The key method on it is BuildChannelFactory, where the WebRequestInterceptorChannelFactory is instantiated.
- WebRequestInterceptorBindingExtensionElement: this is just the class that will allow us to add the previous binding element to our binding configuration (make it show up in the “Add Extension” dialog while configuring our customBinding). It inherits from BindingElementExtensionElement.
- WebRequestInterceptorChannelFactory: This will be the channel factory that will be instantiated by the WebRequestInterceptorBindingElement binding extension and later used to create WebRequestInterceptorChannel channels. The keymethod on this one is OnCreateChannel, where the WebRequestInterceptorChannel will be instantiated. It inherits from ChannelFactoryBase<TChannel>, and specifically from ChannelFactoryBase<IRequestChannel>.
- WebRequestInterceptorChannel: This is our custom channel class, that will directly execute the communication with the target service and handle (if required) any exceptions that might happen. It inherits from ChannelBase, and implements the IRequestChannel interface. The key methods here are EndRequest and Request, as those are the ones where the ProtocolException exceptions are likely to happen while interacting with the target service.
We wrap the corresponding sentences where the ProtocolException would happen (_innerchannel.EndRequest and _innerchannel.Request respectively) with a try/catch block, add the logic that retrieves the information from the Protocol/Web exceptions and the passes back that information as a result of the call. Adding that information to any other exception type won't do the trick (at least not a 100% clean one), as that exception will again be wrapped inside an XlangSoapException, so we need to find another way around it. In this case, we will just send the information back by returning a fault message as a result of the call, and adding the specific HTTP Status code as a message header on that fault message (although we could come up with other approaches to send back that info into BizTalk from the channel, but this one seemed to me like the quickest/simplest one to showcase the solution).
We will also create a Property Schema (in the corresponding BizTalk project) that will allow us to retrieve the message header from the message contexts once it arrives to the orchestration.
Finally, we will just need to deploy the projects (the WCF extension assembly into the GAC and the Schemas project into BizTalk) and modify the machine.config file so BizTalk is aware of our new binding element extension:
<add name="WebRequestInterceptor" type="adrb.CustomWebHttpBinding.WebRequestInterceptorBindingExtensionElement, adrb.CustomWebHttpBinding, Version=1.0.0.0, Culture=neutral, PublicKeyToken=0b236f220eb351a7"/>
Testing the solution
In order to test this solution, we will modify the sample ConsumeGET.odx orchestration include in the bLogical REST Start Kit for BizTalk to check for the existence of that custom WCF message header we add when a ProtocolException happens and capture the status code from it. We need to add a reference to the schemas project (adrb.Schemas) where the property schema was created to define the message header context property.
We will just retrieve the WCF header from the message context of the response message we will receive (that in case of a service failure will be the custom fault message we create in the channel), get the status code from it and suspend the orchestration (but we could be creating an ESB Toolkit fault message that mentions the status code, trace the information in our logs…)
Finally, we need to modify the Send port used in the sample solution to interact with the web services so it uses out custom binding element. I added a binding file into the solution to make it easier for you to apply the configuration, but basically, the changes to make are:
- Change the WCF-Custom adapter binding to customBinding
- Remove the textMessageEncoding binding element and add the webMessageEncoding instead
- Add the custom WebRequestInterceptor extension
- Set the httpTransport element “ManualAddressing” attribute to true
After all that, we are ready to test the solution. We just need to drop the test message that makes the sample REST service a HTTP 400 status code and see how our orchestration instance gets suspended telling us the HTTP Status code that was received from the service. We achieved our goal of being able to effectively retrieve the HTTP status code and so be able to use it in our code J
BizTalk Server 2013 scenario
After consuming the same test service we used above by following the good guide put together by Richard Seroter here on how to use the new WCF-WebHttp adapter that comes with BizTalk 2013, it looks like the XlangSoapException behavior we observed before is still the same in BizTalk 2013, so initially the same customBinding approach followed with BizTalk 2010 would apply here. In any case, I didn't have time yet to explore any other possible alternatives in BizTalk 2013, but I guess we won't be very lucky...
See Also
Read suggested related topics:
- BizTalk Server 2013: REST Support through WCF-WebHttp
- BizTalk Server 2013: Consuming a RESTFul Endpoint using WCF-WebHttp
Another important place to find a huge amount of BizTalk related articles is the TechNet Wiki itself. The best entry point is BizTalk Server Resources on the TechNet Wiki.