How can I send a DataContract object as parameter for a WCF RESTFUL webservice?

marifrahman picture marifrahman · Sep 6, 2013 · Viewed 13.5k times · Source

I am developing a WCF resful service which will be basically consumed from some mobile applications. Over the POST I am trying to send a DataContract object [actually I have to send a List of object] and another single id as string. My question is if it is possibly to define my function to accept DataContract object and the single string ?

Following is my code : Interface declaration:

[ServiceContract]
    public interface IService1
    {

        [OperationContract]
        [WebInvoke(Method = "POST", ResponseFormat = WebMessageFormat.Json, UriTemplate = "GetDataUsingDataContract/{id}")]
        CompositeType GetDataUsingDataContract(string id, CompositeType composite );


    }

    [DataContract]
    public class CompositeType
    {
        bool boolValue = true;
        string stringValue = "Hello ";

        [DataMember]
        public bool BoolValue
        {
            get { return boolValue; }
            set { boolValue = value; }
        }

        [DataMember]
        public string StringValue
        {
            get { return stringValue; }
            set { stringValue = value; }
        }
    }

Actual definition of the function:

public CompositeType GetDataUsingDataContract(string id, CompositeType composite )
        {

            if (composite == null)
            {
                throw new ArgumentNullException("composite");
            }
            if (composite .BoolValue)
            {
                composite .StringValue += "- Suffix and the id is"+id;
            }
            return report;
        }

and the json object I am trying to send from Fiddler is

{"BoolValue":true,"StringValue":"sdfsdfsf"}

Fiddler snap when sending the request Fiddler snap of the output Above are the snaps from the fiddler I am testing the service from. After couple of googling I have got the following link where the client actually uses webservice reference to get the DataContract type and serializes to json before sending as request body. But why then my test from Fiddler doesn't succeed ?! http://dotnetmentors.com/wcf/wcf-rest-service-to-get-or-post-json-data-and-retrieve-json-data-with-datacontract.aspx

Can anybody please suggest anything ?

The web.config is as bellow:

<?xml version="1.0"?>
<configuration>

  <system.web>
    <compilation debug="true" targetFramework="4.0" />
  </system.web>
  <system.serviceModel>
    <services>
      <service name="JSONWebService.Service1" behaviorConfiguration="JSONWebService.Service1Behavior">
        <endpoint address="../Service1.svc"
            binding="webHttpBinding"
            contract="JSONWebService.IService1"
            behaviorConfiguration="webBehaviour" />
      </service>
    </services>
    <behaviors>
      <serviceBehaviors>
        <behavior name="JSONWebService.Service1Behavior">

          <!-- To avoid disclosing metadata information, set the value below to false and remove the metadata endpoint above before deployment -->
          <serviceMetadata httpGetEnabled="true"/>
          <!-- To receive exception details in faults for debugging purposes, set the value below to true.  Set to false before deployment to avoid disclosing exception information -->
          <serviceDebug includeExceptionDetailInFaults="true"/>
        </behavior>
      </serviceBehaviors>
      <endpointBehaviors>
        <behavior name="webBehaviour">
          <webHttp/>
        </behavior>
      </endpointBehaviors>
    </behaviors>
    <serviceHostingEnvironment multipleSiteBindingsEnabled="true" />
  </system.serviceModel>
 <system.webServer>
    <modules runAllManagedModulesForAllRequests="true"/>
  </system.webServer>

</configuration>

Answer

Dominic Betts picture Dominic Betts · Sep 6, 2013

There's a couple of things you need to do to get this scenario to work.

First, in the service contract, you need to mark the method that has multiple parameters with the BodyStyle parameter of the WebInvoke attribute set to Wrapped as shown in this example (adapted from the sample you linked to at http://dotnetmentors.com/wcf/wcf-rest-service-to-get-or-post-json-data-and-retrieve-json-data-with-datacontract.aspx):

[OperationContract]
[WebInvoke(UriTemplate = "/PlaceOrder",
    RequestFormat = WebMessageFormat.Json,
    ResponseFormat = WebMessageFormat.Json, Method = "POST",
    BodyStyle = WebMessageBodyStyle.Wrapped)]
bool PlaceOrder(string id, OrderContract order);

With this attribute parameter in place you have to wrap the multiple web method parameters into a single string in your client. The following example shows a way of doing this in C#:

  var requestdata = new
  {
    id = order.OrderID,
    order = order
  };
  string data2 = JsonConvert.SerializeObject(requestdata);

Note that the field names in the anonymous method match the parameter names in your web method.

For reference, the JSON that this produces looks like this where you can see the id and order objects in the JSON string:

{"id":"10560","order":{"OrderID":"10560","OrderDate":"06/09/2013 12:29:04","ShippedDate":"16/09/2013 12:29:04","ShipCountry":"Uganda","OrderTotal":"781"}}

You should be able to test out JSON in this format in Fiddler with your example.


Extending the answer to deal with other datatypes

Using a DataContract that includes a bool and a DateTime property:

[DataContract]
public class OrderContract
{
  [DataMember]
  public string OrderID { get; set; }

  [DataMember]
  public string OrderDate { get; set; }

  [DataMember]
  public string ShippedDate { get; set; }

  [DataMember]
  public string ShipCountry { get; set; }

  [DataMember]
  public string OrderTotal { get; set; }

  [DataMember]
  public bool Shipped { get; set; }

  [DataMember]
  public DateTime DeliveredDate { get; set; }
}

The issue here is dealing with the DateTime and making sure that the JSON is in a format that the WCF RESTful service can deserialize. To get this to work I used this code on the client:

OrderContract order = new OrderContract
{
  OrderID = "10560",
  OrderDate = DateTime.Now.ToString(),
  ShippedDate = DateTime.Now.AddDays(10).ToString(),
  ShipCountry = "India",
  OrderTotal = "781",
  Shipped = true,
  DeliveredDate = DateTime.Now
};

DataContractJsonSerializer ser =
        new DataContractJsonSerializer(typeof(OrderContract));
MemoryStream mem = new MemoryStream();
ser.WriteObject(mem, order);
string data =
    Encoding.UTF8.GetString(mem.ToArray(), 0, (int)mem.Length);

var requestdata = new
{
  id = order.OrderID,
  order = order
};
JsonSerializerSettings microsoftDateFormatSettings = new JsonSerializerSettings
{
  DateFormatHandling = DateFormatHandling.MicrosoftDateFormat
};
string data2 = JsonConvert.SerializeObject(requestdata, microsoftDateFormatSettings);
WebClient webClient = new WebClient();
webClient.Headers["Content-type"] = "application/json";
webClient.Encoding = Encoding.UTF8;
webClient.UploadString("http://localhost:61966/OrderService.svc/PlaceOrder", "POST", data2);

Note the JSON Serializer settings - this produces the following JSON:

{"id":"10560","order":{"OrderID":"10560","OrderDate":"10/09/2013 16:15:30","ShippedDate":"20/09/2013 16:15:30","ShipCountry":"India","OrderTotal":"781","Shipped":true,"DeliveredDate":"\/Date(1378826130655+0100)\/"}}