I'm trying to understand how to use WCF Data Services (based on EF 4.1) to create a restful web service that will persist entities passed as JSON objects.
I've been able to create a method that can accept a GET request with a set of primitive data types as arguments. I don't like that solution, I would prefer to send a POST request with a JSON object in the http request body.
I've found that I can't get the framework to serialize the json into an object for me, but i would be fine with doing it manually.
My problem is that I can't seem to read the body of the POST request - the body should be the JSON payload.
Here's a rough crack at it below. I've tried a few different iterations of this and can't seem to get the raw JSON out of the request body.
Any thoughts? A better way to do this? I just want to POST some JSON data and process it.
[WebInvoke(Method = "POST")]
public void SaveMyObj()
{
StreamReader r = new StreamReader(HttpContext.Current.Request.InputStream);
string jsonBody = r.ReadToEnd(); // jsonBody is empty!!
JavaScriptSerializer jss = new JavaScriptSerializer();
MyObj o = (MyObj)jss.Deserialize(jsonBody, typeof(MyObj));
// Now do validation, business logic, and persist my object
}
My DataService is an Entity Framework DataService that extends
System.Data.Services.DataService<T>
If I try adding non-primitive values as parameters to the method, i see the following exception in the trace log:
System.InvalidOperationException, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
'Void SaveMyObj(MyNamespace.MyObj)' has a parameter 'MyNamespace.MyObj o' of type 'MyNamespace.MyObj' which is not supported for service operations. Only primitive types are supported as parameters.
Add parameters to your method. You'll also want some additional attributes on your WebInvoke.
Here's an example (from memory so it might be a little off)
[WebInvoke(BodyStyle = WebMessageBodyStyle.Wrapped, RequestFormat = WebMessageFormat.Json, ResponseFormat = WebMessageFormat.Json, UriTemplate = "modifyMyPerson")]
public void Modify(Person person) {
...
}
With person class something like this:
[DataContract]
public class Person {
[DataMember(Order = 0)]
public string FirstName { get; set; }
}
And json sent like this
var person = {FirstName: "Anthony"};
var jsonString = JSON.stringify({person: person});
// Then send this string in post using whatever, I personally use jQuery
EDIT: This is using "wrapped" approach. Without wrapped approach you would take out the BodyStyle = ...
and to stringify the JSON you would just do JSON.stringify(person)
. I just usually use the wrapped methodology in case I ever need to add additional parameters.
EDIT For full code sample
Global.asax
using System;
using System.ServiceModel.Activation;
using System.Web;
using System.Web.Routing;
namespace MyNamespace
{
public class Global : HttpApplication
{
protected void Application_Start(object sender, EventArgs e)
{
RouteTable.Routes.Add(new ServiceRoute("myservice", new WebServiceHostFactory(), typeof(MyService)));
}
}
}
Service.cs
using System;
using System.ServiceModel;
using System.ServiceModel.Activation;
using System.ServiceModel.Web;
namespace MyNamespace
{
[ServiceContract]
[ServiceBehavior(MaxItemsInObjectGraph = int.MaxValue)]
[AspNetCompatibilityRequirements(RequirementsMode = AspNetCompatibilityRequirementsMode.Allowed)]
public class MyService
{
[OperationContract]
[WebInvoke(UriTemplate = "addObject", ResponseFormat = WebMessageFormat.Json, BodyStyle = WebMessageBodyStyle.WrappedRequest)]
public void AddObject(MyObject myObject)
{
// ...
}
[OperationContract]
[WebInvoke(UriTemplate = "updateObject", ResponseFormat = WebMessageFormat.Json, BodyStyle = WebMessageBodyStyle.WrappedRequest)]
public void UpdateObject(MyObject myObject)
{
// ...
}
[OperationContract]
[WebInvoke(UriTemplate = "deleteObject", ResponseFormat = WebMessageFormat.Json, BodyStyle = WebMessageBodyStyle.WrappedRequest)]
public void DeleteObject(Guid myObjectId)
{
// ...
}
}
}
And add this to Web.config
<system.serviceModel>
<serviceHostingEnvironment aspNetCompatibilityEnabled="true" />
</system.serviceModel>