Yes Richard, You Can Use Ampersands in the BizTalk REST Adapter (And Some ASP.NET Web API Tips)

A few months back, I wrote up a pair of blog posts (part 1, part 2) about the new BizTalk Server 2013 REST adapter. Overall, I liked it, but I complained about the  apparent lack of support for using ampersands (&) when calling REST services. That seemed like a pretty big whiff as you find many REST services that use ampersands to add filter parameters and such to GET requests. Thankfully, my readers set me straight. Thanks Henry Houdmont and Sam Vanhoutte! You CAN use ampersands in this adapter, and it’s pretty simple once you know the trick. In this post, I’ll first show you how to consume a REST service that has an ampersand in the URL, and, I’ll show you a big gotcha when consuming ASP.NET Web API services from BizTalk Server.

First off, to demonstrate this I created a new ASP.NET MVC 4 project to hold my Web API service. This service takes in new invoices (and assigns them an invoice number) and returns invoices (based on query parameters). The “model” associated with the service is pretty basic.

public class Invoice
    {
        public string InvoiceNumber { get; set; }
        public DateTime IssueDate { get; set; }
        public float PreviousBalance { get; set; }
        public float CurrentBalance { get; set; }

    }

The controller is the only other thing to add in order to get a working service. My controller is pretty basic as well. Just for fun, I used a non-standard name for my query operation (instead of the standard pattern of Get<model type>) and decorated the method with an attribute that tells the Web API engine to call this operation on GET requests. The POST operation uses the expected naming pattern and therefore doesn’t require any special attributes.

public class InvoicesController : ApiController
    {
        [System.Web.Http.HttpGet]
        public IEnumerable<Invoice> Lookup(string id, string startrange, string endrange)
        {
            //yank out date values; should probably check for not null!
            DateTime start = DateTime.Parse(startrange);
            DateTime end = DateTime.Parse(endrange);

            List<Invoice> invoices = new List<Invoice>();

            //create invoices
            invoices.Add(new Invoice() { InvoiceNumber = "A100", IssueDate = DateTime.Parse("2012-12-01"), PreviousBalance = 1000f, CurrentBalance = 1200f });
            invoices.Add(new Invoice() { InvoiceNumber = "A200", IssueDate = DateTime.Parse("2013-01-01"), PreviousBalance = 1200f, CurrentBalance = 1600f });
            invoices.Add(new Invoice() { InvoiceNumber = "A300", IssueDate = DateTime.Parse("2013-02-01"), PreviousBalance = 1600f, CurrentBalance = 1100f });

            //get invoices within the specified date range
            var matchinginvoices = from i in invoices
                                   where i.IssueDate >= start && i.IssueDate <= end
                                   select i;

            //return any matching invoices
            return matchinginvoices;
        }

        public Invoice PostInvoice(Invoice newInvoice)
        {
            newInvoice.InvoiceNumber = System.Guid.NewGuid().ToString();

            return newInvoice;
        }
    }

That’s it! Notice that I expect the date range to appear as query string parameters, and those will automatically map to the two input parameters in the method signature. I tested this service using Fiddler and could make JSON or XML come back based on which Content-Type HTTP header I sent in.

2013.03.19.rest01

Next, I created a BizTalk Server 2013 project in Visual Studio 2012 and defined a schema that represents the “invoice request” message sent from a source system. It has fields for the account ID, start date, and end date. All those fields were promoted into a property schema so that I could use their values later in the REST adapter.

2013.03.19.rest03

Then I built an orchestration to send the request to the REST adapter. You don’t NEED To use an orchestration, but I wanted to show how the “operation name” on an orchestration port is used within the adapter. Note below that the message is sent to the REST adapter via the “SendQuery” orchestration port operation.

2013.03.19.rest02

In the BizTalk Administration console, I configured the necessary send and receive ports. The send port that calls the ASP.NET Web API service uses the WCF-WebHttp adapter and a custom pipeline that strips out the message of the GET request (example here; note that this will likely be corrected in the final release of BizTalk 2013).

2013.03.19.rest04

In the adapter configuration, notice a few things. See that the “HTTP Method and URL Mapping” section has an entry that maps the orchestration port operation name to a URI. Also, you can see that I use an escaped ampersand (&amp;) in place of an actual ampersand. The latter throws and error, while the former works fine. I mapped the values from the message itself (via the use of a property schema) to the various URL variables.

2013.03.19.rest05

When I started everything up and send a “invoice query” message into BizTalk, I quickly got back an XML document containing all the invoices for that account that were timestamped within the chosen date range.

2013.03.19.rest06

Wonderful. So where’s the big “gotcha” that I promised? When you send a message to an ASP.NET Web API endpoint, the endpoint seems to expect a UTF-8 unless otherwise designated. However, if you use the default XMLTransmit pipeline component on the outbound message, BizTalk applies a UTF-16 encoding. What happens?

2013.03.19.rest07

Ack! The “newInvoice” parameter is null. This took me a while to debug, probably because I’m occasionally incompetent and there were also no errors in the Event Log or elsewhere. Once I figured out that this was an encoding problem, the fix was easy!

The REST adapter is pretty configurable, including the ability to add outbound headers to the HTTP message. This is the HTTP header I added that still caused the error above.

2013.03.19.rest08

I changed this value to also specify which encoding I was sending (charset=utf16).

2013.03.19.rest09

After saving this updated adapter configuration and sending in another “new invoice” message, I got back an invoice with a new (GUID) invoice number.

2013.03.19.rest10

I really enjoy using the ASP.NET Web API, but make sure you’re sending what the REST service expects!

About these ads


Categories: ASP.NET Web API, BizTalk

5 replies

  1. Great Article!

  2. A minor correction. The more standard way to do content negotiation with web API or any HTTP service is to send the Accept header. Sending Content-Type header worked in your case because we fall back to Content-Type header if we don’t find a Accept header in web API. It is kind of weird though to have a Content-Type header without any content (GET requests).

    And regarding the parameter to Post being null, we log deserialization errors in the ModelState property on the controller. Checkout this blog post (specially the ‘Applying Error Handling to Handle Invalid ModelStates’ section) for using an action filter to handle model state errors. http://blogs.msdn.com/b/youssefm/archive/2012/06/28/error-handling-in-asp-net-webapi.aspx

Trackbacks

  1. Automating BizTalk Events with Windows Azure, BizTalk 2013 and ASP.Net | BizTalk Events
  2. Scott Banwart's Blog › Distributed Weekly 199

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

Follow

Get every new post delivered to your Inbox.

Join 262 other followers

%d bloggers like this: