9

In my RESTful WCF Serice I need to pass a class as a parameter for URITemplate. I was able to pass a string or multiple strings as parameters. But I have a lot of fields are there to pass to WCF Service. So I have created a class and added all the fields as properties and then I want to pass this class as one paramenter to the URITemplate. When I am trying to pass class to the URITemplate I am getting error "Path segment must have type string". Its not accepting class as a parameter. Any idea how to pass class as a parameter. Here is my code (inputData is class)


    [OperationContract]
    [WebGet(UriTemplate = "/InsertData/{param1}")]
    string saveData(inputData param1);
Paul Sonier
  • 36,435
  • 3
  • 72
  • 113
Henry
  • 807
  • 6
  • 21
  • 51

2 Answers2

17

You actually can pass a complex type (class) in a GET request, but you need to "teach" WCF how to use it, via a QueryStringConverter. However, you usually shouldn't do that, especially in a method which will change something in the service (GET should be for read-only operations).

The code below shows both passing a complex type in a GET (with a custom QueryStringConverter) and POST (the way it's supposed to be done).

public class StackOverflow_6783264
{
    public class InputData
    {
        public string FirstName;
        public string LastName;
    }
    [ServiceContract]
    public interface ITest
    {
        [OperationContract]
        [WebGet(UriTemplate = "/InsertData?param1={param1}")]
        string saveDataGet(InputData param1);
        [OperationContract]
        [WebInvoke(UriTemplate = "/InsertData")]
        string saveDataPost(InputData param1);
    }
    public class Service : ITest
    {
        public string saveDataGet(InputData param1)
        {
            return "Via GET: " + param1.FirstName + " " + param1.LastName;
        }
        public string saveDataPost(InputData param1)
        {
            return "Via POST: " + param1.FirstName + " " + param1.LastName;
        }
    }
    public class MyQueryStringConverter : QueryStringConverter
    {
        public override bool CanConvert(Type type)
        {
            return (type == typeof(InputData)) || base.CanConvert(type);
        }
        public override object ConvertStringToValue(string parameter, Type parameterType)
        {
            if (parameterType == typeof(InputData))
            {
                string[] parts = parameter.Split(',');
                return new InputData { FirstName = parts[0], LastName = parts[1] };
            }
            else
            {
                return base.ConvertStringToValue(parameter, parameterType);
            }
        }
    }
    public class MyWebHttpBehavior : WebHttpBehavior
    {
        protected override QueryStringConverter GetQueryStringConverter(OperationDescription operationDescription)
        {
            return new MyQueryStringConverter();
        }
    }
    public static void Test()
    {
        string baseAddress = "http://" + Environment.MachineName + ":8000/Service";
        ServiceHost host = new ServiceHost(typeof(Service), new Uri(baseAddress));
        host.AddServiceEndpoint(typeof(ITest), new WebHttpBinding(), "").Behaviors.Add(new MyWebHttpBehavior());
        host.Open();
        Console.WriteLine("Host opened");

        WebClient client = new WebClient();
        Console.WriteLine(client.DownloadString(baseAddress + "/InsertData?param1=John,Doe"));

        client = new WebClient();
        client.Headers[HttpRequestHeader.ContentType] = "application/json";
        Console.WriteLine(client.UploadString(baseAddress + "/InsertData", "{\"FirstName\":\"John\",\"LastName\":\"Doe\"}"));

        Console.Write("Press ENTER to close the host");
        Console.ReadLine();
        host.Close();
    }
}
carlosfigueira
  • 79,156
  • 14
  • 125
  • 167
  • Is it possible to provide a custom QueryStringConverter to a webHttpBehaviour already defined in Web.config? – mmdemirbas Mar 07 '12 at 09:25
  • 1
    No, you need a new class for that. You can expose that class in config if you want, though - see http://blogs.msdn.com/b/carlosfigueira/archive/2011/06/28/wcf-extensibility-behavior-configuration-extensions.aspx for details. – carlosfigueira Mar 07 '12 at 15:10
  • 1
    Your [personal displeasure with configuration](http://blogs.msdn.com/b/carlosfigueira/archive/2011/06/14/wcf-extensibility-servicehostfactory.aspx) helped me. Thanks. – mmdemirbas Mar 08 '12 at 14:42
5

Passing a class (data contract) is only possible with POST or PUT request (WebInvoke). GET request allows only simple types where each must be part of UriTemplate to be mapped to parameter in the method.

Ladislav Mrnka
  • 349,807
  • 56
  • 643
  • 654
  • I tried using webinvoke with post method. Still its not working. here is my code ************ [OperationContract] [WebInvoke(UriTemplate= "/InsertData/param1",Method="POST")] string saveData(inputData param1); Here is how I am passing values to WCF service http://localhost:64475/RESTService1.svc/insertData/1004,120Jackson%20blvd,Newyork,ny,10001,Bronx in Browser – Henry Jul 21 '11 at 22:35
  • 1
    The class must be in request's body. Not in URL. – Ladislav Mrnka Jul 21 '11 at 23:28
  • 1
    is it possible, can you provide some code or example. Thanks for ur time – Henry Jul 21 '11 at 23:43