tags:

views:

73

answers:

3

I have successfully added and used a Get action in my new REST-service in .Net using WCF and the Rest-toolkit. The service is defined like this:

[OperationContract]
[WebGet(UriTemplate = "/{id}")]
Foo GetFooById(string id);

And I call it like this from the client side:

public Foo GetFoo(string id)
{
    var httpClient = new HttpClient("http://127.0.0.1:8000/");
    var response = httpClient.Get("foo/" + id);
    return response.Content.ReadAsDataContract<Foo>();
}

Now I want to add a POST action, but how do you define it, and how do you map the parameters?

A: 

I believe you wrap it in XML. It is discussed here (answer provided):

http://social.msdn.microsoft.com/Forums/en-US/wcf/thread/00c93f9f-f1f3-4f08-9927-db0e4ed91d2d

Also you need to specify the request Content-Type as application/xml. This is how you would also pass custom classes (the XML representation of those classes).

You define it using the WebInvoke attribute, very similar to how you have doen with the WebGet. However, WebInvoke doesn't use the URL placeholders that you've used in the WebGet.

Adam
+2  A: 

You need the WebInvoke attribute instead of WebGet:

[OperationContract]
[[WebInvoke(Method = "POST", UriTemplate = "/{id}")]
Foo PutFooById(string id, Foo foo)

Note that "POST" is actually the default method, so it can be omitted if you desire.

Dan Puzey
Thanks, and I Actually got this part. My problem is how to pass the post data. For that function - how do you pass foo? How do you add the post-data on the client side, and how will the service now to address it to the "foo"-parameter?
stiank81
IIRC (it's been a while) you post a serialized `Foo` as your message body in the `POST`. I'll check it out later if you need it confirmed in more detail.
Dan Puzey
Okay - but what if you want to post more than one argument? E.g. one foo and two strings. How does it map them? I'm assuming you need to name them somehow?
stiank81
You can only pass a single root Xml element in the POST body. So, you either have extra uri parameters (so your `UriTemplate` might be `"/{id}/{name}/?filter={filterParam}"` or something) - or, you create a class to hold your multiple parameters and pass a single instance of that. For example, you might create a class called `FooRequest` that has properties for both a `Foo` and an `Id` a `Name`, and then pass a single parameter of that type.
Dan Puzey
Sure - it's okay that there can only be a single Xml root element in the post body, but I don't understand why it can't pack multiple data objects inside this root. I mean; I'd like to have a class for building my post body where I can say someClass.AddParameter("paramName",param); Is there no such thing? In my case it doesn't feel right to pass the data as uri-parameters, and I don't want to create new request-classes for every post-service. So; can it be done? Can I put multiple parameters into the Xml root without creating a new class for holding these?
stiank81
Nevermind - @DarrelMiller clearified it in a comment to his answer. Thanks for your help.
stiank81
You can, but if you do then you're looking at custom-deserializing the Xml on the server-side, which is a whole heap more pain (and code). One option is to define a loosely-typed dictionary Xml, but that loses you any type-checking or schema validation. So, the better idea is to have something strongly typed to hold your request parameters - and the easiest way to generate this kind of Xml (with a root node and several strongly-typed children) is to serialize a lightweight class. Creating request-classes for complex post services is fairly standard practise, and for good reason!
Dan Puzey
@Dan The best option really is to use a standardized media type like `application/x-www-form-urlencoded` which is designed specifically to hold name value pairs. Unfortunately, WCF makes it much harder than it should be to use standard media types.
Darrel Miller
+2  A: 

In order to POST parameters, you need to serialize it using the DataContractSerializer. e.g,

On server:

[OperationContract]
[WebInvoke(Method="POST",UriTemplate = "/foos")]
void PostFoo(Foo foo) {}

On client:

var foo = new Foo();
var content = HttpContentExtensions.CreateDataContract<Foo>(foo);
var client = new HttpClient("http://example.org/service.svc/foos");
client.Post(content)

Please note, no compiler was involved during the creation of this code, buyer beware.

Darrel Miller
Thanks! Sounds promissing. But how does it know which parameter the foo object should go to? In this case it's obvious, but what if there were two parameters of type foo - or string for that matter. Can you name the parameters somehow?
stiank81
The last parameter is the body of the message. The other parameter will matched by name based on the URI template.
Darrel Miller
"The last parameter is the body of the message" - Is this a general rule? And can't you pass more than one post parameter? Naming them somehow when calling the service..?
stiank81
@stiank81 You can only map one parameter to the body.
Darrel Miller
What you really want is to use a media type like application/x-www-form-urlencoded. The problem is that using that media type is a pain in WCF. See this question: http://stackoverflow.com/questions/604463/best-way-to-support-application-x-www-form-urlencoded-post-data-with-wcf
Darrel Miller
Okay - thanks for clearifying this. I guess I'll use some sort of value objects to hold the data within the post parameter. Will check out the linked question. Thanks for your help!
stiank81