Previous posts in this series:
Like all Internet based businesses, our Web Bagels company has become wildly successful. This, of course, has resulted in Venture Capitalists throwing obscene amounts of cash at us. There is only one thing to do: expand. In addition to our retail delivery business we now want to start supplying local grocery stores, coffee shops and restaurants with our bagels. To accommodate this, we also have expanded our physical resources and now have several smaller commercial bakeries scattered across the region. Each satellite kitchen is responsible for one or more commercial accounts (baking and billing). We want this to be somewhat transparent to our customers; we don’t want them to have to specify (or even have to know) the specific bakery that will be producing their bagels. Another design consideration is that with each order the customer will be sending super-secret payment information to cover the cost of the bagels, which requires that the message be encrypted. We also have the issue that our clients are running on a variety of platforms, with different messaging requirements and conventions. So, while we want all the commercial clients to send their orders to the same service, the service needs to be able to route the message to the appropriate bakery without being able or needing to read the message.
Sounds difficult? Not really.
Getting SOAPy
SOAP messages, which are the types of messages we have been working with all this time, use a message layout paradigm similar to an envelope. When you send a letter through the snail-mail (do people still do this?) you would write on the envelope all the information that the Post Office needed to deliver that message to the intended recipient. The mail carrier didn’t need to open your envelope and read the letter to figure out where it needed to go.
A SOAP message is an XML document that has two “main” sections: the header and the body. The header is equivalent to the envelope you put your letter in and the body is the letter. So what does that mean to our service implementation? Consider the following topology:
This diagram represents the basic topology of our service infrastructure. Our clients will send their requests to the public service. The message then has to be routed, without actually reading the message, to either Bakery A or Bakery B. The Public Service is what’s referred to an an “intermediary service.” It’s job is to accept messages, perhaps perform some processing based on the message, and to send it along to another service, where the ultimate goal is for the message to reach it’s final destination. If, as is our case, the intermediary service simply needs to route the message to the next service then it doesn’t need to look at the body; the header should have all the information that’s needed for the service to figure out where the message needs to go. You may know this concept as WS-Addressing and is a very important standard in SOA implementations.
Back to Bagels
For our contrived example need to create a new data contract for corporate orders. Add a new class called CorporateOrder to the WebBagles project and add the fields as shown here:
This is pretty much the same order information that our WebBagles service takes in for orders now. To accommodate the commercial accounts we’ve added the SuperSecretPaymentData field as well as the DeliveryLocation field which is a customer location code number for the store we are to deliver the bagels to.
Note: We will not be encrypting the message in this demonstration. Security will be covered in a future post. For now, we will pretend that the message is encrypted.
It’s time to introduce the Data Contracts cousin; the Message Contract. Like the Data Contract, the Message Contract tells .NET how to format messages to be sent with WCF. Whereas the Data Contract defined how we want the messages body to look, the Message Contract allows us to control what fields are written to the body of the message and which are written to the header. Will use the same declarative model to let WCF know that CorporateOrder is a Message Contract. For now, we’ll just write everything to the body by decorating the fields with the MessageBodyMember attribute:
Remember, pretty much everything in WCF works on the opt-in model. This means that we must decorate ALL the fields on the class that we want to be included in the message. If we don’t decorate a field, it gets ignored.
One wrinkle we encounter when using Message Contracts as parameters for our service methods is that we must also return a Message Contract or void; no other primitive types and no Data Contracts. Let’s create a return message contract. Right click the WebBagles project and add a new class called CorporateResponse. Add the following code:
Just because we can’t directly return Data Contracts from our service method doesn’t mean we can’t return Data Contracts from our service method at all. We just need to wrap them in a Message Contract. We already have all the response information defined in BagelOrderResponse. It wouldn’t make sense to redefine it. It’s easier to simply define our message as returning an instance of BagelOrderResponse in the message body.
You can wrap as many Data Contracts in a Message Contract you want, just remember that you CAN NOT put a message contract in a Data Contract or in another Message Contract. The Message Contract MUST be at the top of the abstraction layers. And remember; there can only be one Message Contract in your message stack.
Now we need to add a new method to our service to consume this message. Open the IBagelOrderService interface and add a method called PlaceCorporateOrder so that it appears as follows:
Now, a question you may be asking yourself is “Hey, why did he use a different method name? Why not just overload PlaceOrder?” The reason is that services don’t use method names like we think of them in .NET. They use something called “Actions” which WCF abstracts away from us, so that 99% of the time we don’t need to worry about them. This is done in the interest of interoperability, and in this paradigm there is really no good, reliable way to differentiate web service methods (actions) with the same name based on the signature. Therefore we need to name every action on a web service with a unique name. There is a way we can use overridden method names in our .NET contracts while giving them unique action names, and it will be covered in a future post. For now we’ll just change the name of the method in our contract.
The next step is to implement the new method in our service class. Add the following method to the BagelOrderService class:
This is just a stub implementation that we’ll use for now, we’ll make changes in a few minutes that show how we deal with message contracts, but you can already see that we are able to access the fields on order (an instance of our Message Contract CorporateOrder) the same way we use Data Contracts. Make sure that the WebBagels project is default running project (right click the WebBagles project, select “Set As Startup Project”) and hit F5 and… you’ll get a build error:
Remember; we added a method to the BagelOrderService. Our custom BagelOrderServiceClient uses the same .NET code to define it’s methods unlike the other two proxies (right click, add service reference and SVCUTIL) which use the WSDL emitted from the service. This is easily fixed though, just implement the method in the proxy:
It’s as easy as that. Our Client doesn’t need to know that CorporateOrder is a Message Contract; WCF insulates us from that implementation detail. Now that that’s taken care of, we can hit F5 and take a look at how our Message Contract is being used. You’ll notice that our new method is listed in the WCF test client:
Will invoke this method with some test data:
And invoke the method. It returns as expected and if we take a look at the XML of the outgoing method we don’t see anything too different. The data is all in the message body, albeit in a slightly different XML wrapper as before:
The point of this demo was to get some of that data, specifically the delivery location, into the header. Let’s go back to our CorporateOrder class and make a that change. Right now Delivery Location is set as a Message Body Member. That attribute, as you can see, tells WCF to write our data to the messages body. Let’s change that attribute to Message Header:
That’s all we had to do. WCF now knows that when this message is serialized DeliveryLocation, and any other field decorated with the MessageHeader attribute should be written to the SOAP header, not the body.
Let’s change our service to look at that value and route our order accordingly. In this case we have two bakeries. If the delivery location code is even, it goes to bakery A, otherwise it goes to bakery B. Again notice that I’m simply accessing a field on the order object. I don’t have to know that it’s part of the header or do anything special to deal with that fact:
Each RouteToBakery method simply calls a method with the bagel processing logic we’ve been using, but provides a hard-coded confirmation number so we can see that the message was routed correctly:
When we run the application the input for our method doesn’t look any different; the same fields are listed and there’s no indication what goes in the header and what goes in the body:
If we supply this method with some data and call it though, we can see that our Delivery Location is now being delivered as part of the head and is no longer part of the message body:
When to Message
As you’ve seen in previous installments of this series Message Contracts are not required to create and consume WCF services. Their job is to provide a simple, easy to use way to customize your SOAP messages both for security concerns and to conform to various interoperability standards. You may also find them handy if you have an existing corporate SOA standard that you must work in. Message Contracts are powerful tools (we’ve only scratched the surface here) and like all powerful tools should be treated with respect and used when needed. Unless you have a need to define what goes in a message body and what goes in a header, you’re OK sticking with Data Contracts.
Like Service and Data Contracts, there are many more customization points in Message Contracts than we’ve explored here. They will be covered in a future post. For now, stick with the basics and you’ll be able to control how your data is sent across the wire.
Print | posted on Tuesday, June 15, 2010 10:42 AM