wcf get method - how to bind to view model? - c#

This is what I have in the service contract:
[WebInvoke(Method = "GET", ResponseFormat = WebMessageFormat.Json,
BodyStyle = WebMessageBodyStyle.WrappedRequest)]
[OperationContract]
string HelloWorld(HelloWorldViewModel vm);
where HelloWorldViewModel has properties X and Y.
If I do localhost/webservices/HelloWorld?X=1&Y=2, and set a breakpoint in the HelloWorld method, vm will be null. It does not automatically bind the passed-in query string params into a view model object.
Am I missing something? Thanks!

The behavior you described is implemented specifically in ASP.NET MVC model binding.
If you want to pass a complex object to a REST service operation using the WCF Web Programming Model, you'll have to include it in its serialized form in the body of an HTTP POST request.
In your case, based on the attributes placed on the HelloWorld service operation, the request's payload should look something like this (note that XML namespace declarations are omitted):
<HelloWorld>
<vm>
<X>1</X>
<Y>2</Y>
</vm>
</HelloWorld>
Related resources:
WebMessageBodyStyle Enumeration

vm == null is correct. It have to be sent as media parameter to your uri to be non null.
To get your parameters try to inspect:
WebOperationContext.Current.IncomingRequest.UriTemplateMatch.QueryParameters["X"]
or ...QueryParameters["Y"]

WCF [WebGet] operations only support primitive types by default. If you want to add support for other types, you can either create a type converter or a new QueryStringConverter that supports your type. You can find more information at http://blogs.msdn.com/b/carlosfigueira/archive/2011/08/09/wcf-extensibility-querystringconverter.aspx.
Another option would be to pass the parameter in the request body, but that would mean using a HTTP verb other than "GET", since GET requests cannot have body.

Related

Unable to pass a Dictionary as a parameter to WCF REST service in GET type of method

I have a requirement where most of WCF REST apis are of type GET and would receive Dictionary as a param. But when it is put as below, it throws error.
[WebGet(UriTemplate = "/GetVersion?Param1={serverDetails}")]
public Version GetVersion(Dictionary<string, string> keyValue)
{
}
It gives below error:
Operation 'GetVersion' in contract 'IDeploy' has a query variable named 'keyValue' of type 'System.Collections.Generic.Dictionary'2[System.String,System.String]', but type 'System.Collections.Generic.Dictionary'2[System.String,System.String]' is not convertible by 'QueryStringConverter'. Variables for UriTemplate query values must have types that can be converted by 'QueryStringConverter'.
Any idea how to resolve this? it would be hard to replace Dictionary param type as there are lots of such methods in service.
For supporting such complex parameters, The Get request cannot accomplish it. We need Post/Put/Delete request to complete this task since we can set up these complex parameters in the request body.
[OperationContract]
[WebInvoke(RequestFormat =WebMessageFormat.Json,BodyStyle =WebMessageBodyStyle.Bare)]
string GetData(Dictionary<string,string> value);
Below is the JSON content of the sample request. 
[{
"Key":"Key1",
"Value":"World"
},
{
"Key":"Key2",
"Value":"Hello"
}]
Below is the Xml content of the sample request when set to WebMessageFormat.Xml
<ArrayOfKeyValueOfstringstring xmlns="http://schemas.microsoft.com/2003/10/Serialization/Arrays">
<KeyValueOfstringstring>
<Key>Key1</Key>
<Value>Hello</Value>
</KeyValueOfstringstring>
<KeyValueOfstringstring>
<Key>Key2</Key>
<Value>World</Value>
</KeyValueOfstringstring>
</ArrayOfKeyValueOfstringstring>
Feel free to let me know if the problem still exists.

Customized message for endpoints not found in WCF Rest Service

I am using WCF to create a ReSTful service. Say my OperationContract is like this:
[OperationContract]
[WebGet(
ResponseFormat = WebMessageFormat.Json,
BodyStyle = WebMessageBodyStyle.Wrapped,
UriTemplate = "GetItemList/{token}/{value}"
)]
Stream GetItemList(string token);
So when I call http://example.com/Service1.svc/GetItemList/myToken/myValue the service will be called.
Now I want to write a default method saying something link, 'No Method/End point exists', wen the user calls
http://example.com/Service1.svc/GetItemList/myToken/ or
http://example.com/Service1.svc/GetItemList/myValue/ or
http://example.com/Service1.svc/GetItemList/
How can I implement that in my code?
Right now what I'm doing is like:
[OperationContract]
[WebGet(
ResponseFormat = WebMessageFormat.Json,
BodyStyle = WebMessageBodyStyle.Wrapped,
UriTemplate = "GetItemList/"
)]
string ValidateItemList();
[OperationContract]
[WebGet(
ResponseFormat = WebMessageFormat.Json,
BodyStyle = WebMessageBodyStyle.Wrapped,
UriTemplate = "GetItemList/{Val1}"
)]
string ValidateItemList(string Val1);
And in the function, I just return a string saying "No method Exists".
Is there a better way? Can I customize error messages, for urls that are directed to my service? Say, a common message for all non existing url requests, that are directed to my service?
Basically, the architecture of WCF attempts to abstract the low level components of any protocol (in this case http) away so you don't have to worry about these types of details. Unfortunate;y, that engine does not expose a nice way to handle the request's that the engine cannot route correctly.
In this case, the URI cannot be "dispatched" to the correct contract implementing class. the engine has these wonderful components called, wait for it, Dispatchers, which can be customized within the wcf framework, either by configuration or programmatically. The problem is that they are a serious pain to implement. I have implemented the unexpected message dispatcher in my answer to another question, listed below:
Handling Invalid URI passed to a WCF service
Let me know if you need any further information!
I didn't find any feasible solution for my above problem. But I have found an alternate option. With VS 2012, Microsoft has released ASP.NET Web API. Which, they say, is the next version of WCF Rest. So in thet, you could handle these situations very easily. We can use the routing method in it.
Anyone intrested for that solution, can look into this post for a head start: http://www.asp.net/web-api/overview/web-api-routing-and-actions/routing-in-aspnet-web-api

POST Multiple Parameters to WCF Service

I'm trying to understand WCF, so my questions may be dumb. I believe I have a firm understanding of "GET" operations. I'm now working on some "POST" operations. My question is, can I write a WCF Service operation, with WebInvoke, that accepts multiple parameters? Or, when I POST data, will it only accept a single serialized parameter?
Thank you!
Yes, but your POST will have to be passed in using a common understanding of the data, aka a "data contract".
In WCF, the typical approach here is that you'd create a contract class (just an off-my-head example, not 100% working))
[DataContract(Namespace="http://yournamespace.com")]
public class MyContract
{
[DataMember(Order=1)]
public string MyData1 { get(); set{};}
[DataMember(order=2)]
public string MyData2 { get(); set{};}
}
Then you'd specify your WCF operation to accept that contract type as its parameter
[WebInvoke(method="POST")]
public string DoSomethingFromPost(MyContract postedData)
{
}
On your client, you'd serialize the data to an xml/json that matches your contract. Again, loose example:
<MyContract xmlns="http://yournamespace.com">
<MyData1>value</MyData1>
<MyData2>value</MyData2>
</MyContract>
When the contract matches, WCF will deserialze your POST into your contract object, at which point you can use it like any other class.
It seems like there is a bit of confusion between wcf (which is the name given to microsofts overall abstraction for network programming) and a specific protocol HTTP, that defines verbs like "POST" and "GET", that wcf will be using to communicate.
When you define a wcf service operation and attribute it with [WebInvoke] you are going to access to the service using REST over HTTP. See webinvoke for more detail, however the remarks sum it up well
The WebInvokeAttribute attribute is
applied to a service operation in
addition to the
OperationContractAttribute and
associates the operation with a
UriTemplate as well as an underlying
transport verb that represents an
invocation (for example, HTTP POST,
PUT, or DELETE). The
WebInvokeAttribute attribute is a
passive operation behavior (the
IOperationBehavior methods do nothing)
that adds metadata to the operation
description. Applying the
WebInvokeAttribute attribute to a
service operation has no effect unless
a behavior that looks for this
metadata in the operation description
(such as WebHttpBehavior) is added to
the service's behavior collection. The
WebInvokeAttribute determines what
HTTP method that a service operation
responds to. By default, all methods
that have the WebInvokeAttribute
applied respond to POST requests.
Also further down the article defines how to map values to your service contract. Something like..
[OperationContract]
[WebInvoke(Method = "POST", UriTemplate = "Mod?x={x}&y={y}")]
long Mod(long x, long y);
EDIT: To make this a bit more informative for people new to the field.
an intro into REST without going into platform specifics.
and a how to set up a simple REST style wcf service using wcf

Why does the WCF 3.5 REST Starter Kit do this?

I am setting up a REST endpoint that looks like the following:
[WebInvoke(Method = "POST", UriTemplate = "?format=json", BodyStyle = WebMessageBodyStyle.WrappedRequest, ResponseFormat = WebMessageFormat.Json)]
and
[WebInvoke(Method = "DELETE", UriTemplate = "?token={token}&format=json", ResponseFormat = WebMessageFormat.Json)]
The above throws the following error:
UriTemplateTable does not support '?format=json' and '?token={token}&format=json' since they are not equivalent, but cannot be disambiguated because they have equivalent paths and the same common literal values for the query string. See the documentation for UriTemplateTable for more detail.
I am not an expert at WCF, but I would imagine that it should map first by the HTTP Method and then by the URI Template. It appears to be backwards. If both of my URI templates are:
?token={token}&format=json
This works because they are equivalent and it then appears to look at the HTTP Method where one is POST and the other is DELETE.
Is REST supposed to work this way? Why are the URI Template Tables not being sorted first by HTTP Method and then by URI Template? This can cause some serious frustrations when 1 HTTP Method requires a parameter and another does not, or if I want to do optional parameters (e.g. if the 'format' parameter is not passed, default to XML).
I believe this is simply a limitation of the routing capability of the UriTemplateTable. This is not a REST issue, just a WCF one I'm afraid.
Have you tried replicating the error in .Net 4.0? They seem to have done quite a bit of work to further support REST scenarios in .Net 4.
To fix this I had to do the following with my POST method:
[WebInvoke(Method = "POST", UriTemplate = "?token={token}&format=json", BodyStyle = WebMessageBodyStyle.WrappedRequest, ResponseFormat = WebMessageFormat.Json)]
My method declaration then took in an additional parameter called 'string token'. I then just ignore the value of 'token' in my method. If the client does not pass a value for token, WCF passes a null string, but since I am not working with it, it did not matter.
This is still frustrating with WCF 3.5, but it is a good workaround if anyone else runs into this.

How would I add new data via a REST service opposed to RPC style service?

I'm still learning about REST and, in my test, came up with this scenario I don't know how to deal with.
I have an existing sample WCF service which uses Linq-to-Sql. Its a tremendously simple database with a single table called "Tasks" which has four fields: Id, Description, IsCompleted, and EnteredDate. (I mentioned this because I have no data contracts defined in the service itself, it all comes from the Context created by Linq.)
Getting data was trivial to convert to REST... as was deleting data. However, inserting new records doesn't seem as easy.
My RPC-style contract operation looks like this:
[OperationContract]
void AddTask(string description);
The Id, IsCompleted, and EnteredDate are not needed as the service implementation looks like this:
public void AddTask(string description)
{
TaskListLinqDataContext db = new TaskListLinqDataContext();
Task task = new Task()
{ Description = description, IsCompleted = false,
EntryDate = DateTime.Now };
db.Tasks.InsertOnSubmit(task);
db.SubmitChanges();
}
The Id is an Identity and therefore handled by the database.
My first thought was to decorate the Operation contract like this:
[WebInvoke(Method="PUT", UriTemplate="tasks/{description}")]
[OperationContract]
void AddTask(string description);
But I don't really know how to get this to work. When I try using Fiddler to add this it returns a result of 411 (Length Required).
What would be the proper way to do this? Will I have to re-write the implementation to accept a whole XML document representing the new record?
Results:
I finally found a nice blog post that helped me resolve this. It turns out I was pretty much doing things correctly but I was putting the test into Fiddler improperly. I also added a few more details to my attribute.
[WebInvoke(RequestFormat = WebMessageFormat.Xml,
ResponseFormat = WebMessageFormat.Xml,
Method = "POST", UriTemplate = "tasks/{description}",
BodyStyle = WebMessageBodyStyle.Bare)]
[OperationContract]
void AddTask(string description);
When I put my Uri into Fiddler it needed to look like this:
http://ipv4.fiddler:8054/tasks/this+is+a+sample+desc+that+works
Now my service correctly accepts the data and can add the task to the database.
Take a look at the WCF REST Starter Kit, which can be downloaded from CodePlex here.
I think this pretty much describes how to add an item to a container in a restful way.
Depending on the requirements, you could simply enable regular CRUD via Astoria? I have a series covering how to do this with LINQ-to-SQL here. Just a thought.
If you want to do a RESTful web service then don't use a WCF application. Modify your URI template to something like Task/new and put the data of the new task in the body of the POST http request. I implement RESTful web services in ASP.NET as generic handlers (*.ashx). This allow me to ready the http method and go from there.
kevin

Categories

Resources