views:

480

answers:

2

Let's say I have a WCF contract such as

[ServiceContract]
public interface IContract
{
    [OperationContract]
    [WebInvoke(Method="POST", RequestFormat=WebMessageFormat.Xml, BodyStyle=WebMessageBodyStyle.Wrapped)]
    string ComplexPost(string name, ComplexType data);
}

And a data contract:

[DataContract(Name="ComplexType", Namespace="")] // don't know if those atts are req'd or not but saw them in another example
public class ComplexType
{
    public string StringData { get; set; }
    public int IntData { get; set; }
    public DateTime DateValue { get; set; }
}

I know that the request has to be wrapped in order to have more than one input parameter, obviously you cannot have an XML document with more than one root.

The problem is, the documentation is really sparse about what you have to wrap it WITH.

The goal is not to consume this with WCF, it needs to be accessible to clients as a generic XML web service. They may not have any knowledge of WCF or even use .NET.

Obviously I can tell from the exceptions I've been receiving that it is expecting an Element with name ComplexPost (the same as the method name) with namespace http://tempuri.org/ because I haven't specified anything else yet, so I know I need this:

<ComplexPost xmlns="http://tempuri.org/"&gt;

...what exactly?

</ComplexPost>

The goal is not specifically to adhere to REST principles. I'm not looking for answers that embed some of the parameters in the UriTemplate leaving only one complex object, making Bare work. I'm looking for a generalized solution to including all of the data within the POST body.

Found with Fiddler

Here is approximately what the result would be, based on what I found using Fiddler.

<ComplexPost xmlns="http://tempuri.org/"&gt;
    <name>Value of name parameter</name>
    <data xmlns:i="http://www.w3.org/2001/XMLSchema-instance"&gt;
        <StringData>The StringData property value</StringData>
        <IntData>10</IntData>
        <DateValue>2009-07-07T15:38:39.7738012-05:00</DateValue>
    </data>
<ComplexPost>

So the wrapper element is the method name, with the XML namespace that's specified on your ServiceContract, or the temppuri.org if not.

Each parameter becomes, in order, a child element, with base types serialized very easily into string representations.

I did my tests with nullable values, including a nullable DateTime. Apparently the declaration that xmlns:i="http://www.w3.org/2001/XMLSchema-instance" must occur before it's needed, and then for a null value, it's a closed element with i:nil="true", for example

<NullableDateProperty i:nil="true" />

It was also very easy for a DataContract object to have child complex types, or even a list of complex objects.

....
<ChildObjectPropertyName>
    <PropertyOfChildObject>value</PropertyOfChildObject>
    ...
</ChildObjectPropertyName>
<ListProperty>
    <ObjectType>
        <ObjectProperty>value</ObjectProperty>
        ....
    </ObjectType>
    ...
</ListProperty>

This may not be complete, but the lesson is definitely get a WCF client working and then examine the exchanges with Fiddler. Thank you Cheeso!

+1  A: 

If I were doing this, publishing a WCF-based REST/POX service, and I Wanted to make it consumable by "anyone and anything" I would use a debugging HTTP Proxy, like Fiddler2, and capture a successful request going from a WCF REST client to the WCF REST Server.

That can be the basis for the beginning of documentation and examples, as well as a set of scripted tests.

There's also a way to turn on message tracing in WCF itself, but I don't know it. I always use Fiddler. In fact I have it open right now.

Cheeso
Using Fiddler2 worked great! I'll edit the question to show what I found.
David
+1  A: 

I don't quite understand your goal here.....

A "normal" WCF service is a SOAP service - which is inherently interoperable with anything else that speaks SOAP - e.g. Java, PHP - you name it. Couldn't you just create your WCF service and accompanying DataContract to be a full-fledged SOAP service and interoperate with your clients?

If not - what will basically happen if you use REST, is that your DataContract (the data representation of your service) will get serialized into XML by the DataContractSerializer. You can easily test this manually in your code with this snippet:

DataContractSerializer dcs = new DataContractSerializer(typeof(YourDataContract));

FileStream outputFile = new FileStream(@"C:\output.xml");
dcs.WriteObject(outputFile, yourTestDataInstance);

The serialized output of "yourTestDataInstance" (of type "YourDataContract") will be written to C:\output.xml and is ready for inspection.

And of course, you can see the REST result when you browse to the service URL. So you might need to tweak the result a bit - but you should be easily able to achieve what you're interested in.

The "Name=" attribute on the DataContract allows you to specify a different name for the root element in the XML - e.g. if you want "Root" instead of "YourDataContract", use the "Name=Root" attribute.

The Namespace= attribute is intended to allow you to put your data contract into a separate XML namespace of your own - just like a .NET namespace, it allows you to disambiguate your data, making it totally unique, and it makes sure your own "Customer" will not collide with another XML element called "Customer" from some other place.

Both attributes are optional - you don't need to specify them if you don't want to.

Marc

marc_s