locked
Passing a complex class in a WebGet? RRS feed

  • Question

  • Hi,

    We are trying to pass a complex type into a web service via WebGet, is this possible with WCF 4?

    This is the the definition in the interface:

            [WebGet(UriTemplate = "Query/{Query}")]
            ServiceData.QueryResponse Query(ServiceData.InputQuery Query);

    This is the class definition:

        [DataContract]
        public class InputQuery
        {
            [DataMember]
            public int StartAt { get; set; }
            [DataMember]
            public int Count { get; set; }
            [DataMember]
            public string Language { get; set; }
            [DataMember]
            public string Engine { get; set; }
            [DataMember]
            public IList<String> ContentSourceString { get; set; }
            [DataMember]
            public string FreeFormString { get; set; }
            [DataMember]
            public IList<Filter> Filters { get; set; }
        }

        [DataContract]
        public class Filter
        {
            [DataMember]
            public string FilterString { get; set; }
            [DataMember]
            public string Operator { get; set; }
        }

    This works well in SOAP, but we would like to support REST also.

    Thank you!

    Wednesday, January 25, 2012 11:48 PM

Answers

  • I added a method to the data class to serialize the input to a string and I am passing that into a new web method. This requires a separate method for the REST callers, but it deserilizes the data and calls the existing method internally.

    It works well and seems to be quite fast!

    • Marked as answer by Tammy King Thursday, January 26, 2012 7:33 PM
    Thursday, January 26, 2012 7:33 PM

All replies

  • Hello, you can't use complex types for parameters in WebGet. All parameters in WebGet represent URL query strings. Think how do you pass a complex in query string or a sub path in the URL. You're required to put every property in a separate query string component. So you have to write:

    [WebGet(UriTemplate = "Query?startat={startAt}&count={count}&...")]
    ServiceData.QueryResponse Query(int startAt, int count, ...);


    Lante, shanaolanxing This posting is provided "AS IS" with no warranties, and confers no rights.
    If you have feedback about forum business, please contact msdnmg@microsoft.com. But please do not ask technical questions in the email.
    Thursday, January 26, 2012 11:45 AM
  • I added a method to the data class to serialize the input to a string and I am passing that into a new web method. This requires a separate method for the REST callers, but it deserilizes the data and calls the existing method internally.

    It works well and seems to be quite fast!

    • Marked as answer by Tammy King Thursday, January 26, 2012 7:33 PM
    Thursday, January 26, 2012 7:33 PM
  • You actually can pass complex types in query strings, but you'll need to write a custom query string converter for it. The post at http://blogs.msdn.com/b/carlosfigueira/archive/2011/08/09/wcf-extensibility-querystringconverter.aspx has more information about it, and the code below shows how this structure can be passed as the query string.

        public class Post_835dec2e_155c_4562_b50d_574e4fefea36
        {
            [ServiceContract]
            public class Service
            {
                [WebGet(UriTemplate = "Query?query={Query}", ResponseFormat = WebMessageFormat.Json)]
                public QueryResponse Query(InputQuery Query)
                {
                    return new QueryResponse
                    {
                        Text = Query.ToQueryString(),
                    };
                }
            }
    
            [DataContract]
            public class QueryResponse
            {
                [DataMember]
                public string Text;
            }
    
            [DataContract]
            public class InputQuery
            {
                [DataMember]
                public int StartAt { get; set; }
                [DataMember]
                public int Count { get; set; }
                [DataMember]
                public string Language { get; set; }
                [DataMember]
                public string Engine { get; set; }
                [DataMember]
                public IList<String> ContentSourceString { get; set; }
                [DataMember]
                public string FreeFormString { get; set; }
                [DataMember]
                public IList<Filter> Filters { get; set; }
    
                static readonly Regex regex = new Regex(@"(?<startat>\d+)\|" +
                    @"(?<count>\d+)\|" +
                    @"(?<language>[^|]+)\|" +
                    @"(?<engine>[^|]+)\|" +
                    @"(?<contentsourcestring>[^|]+)\|" +
                    @"(?<freeformstring>[^|]+)\|" +
                    @"(?<filters>[^|]+)");
    
                public string ToQueryString()
                {
                    StringBuilder sb = new StringBuilder();
                    sb.Append(StartAt);
                    sb.Append("|");
                    sb.Append(Count);
                    sb.Append("|");
                    sb.Append(Language);
                    sb.Append("|");
                    sb.Append(Engine);
                    sb.Append("|");
                    sb.Append(string.Join(",", ContentSourceString));
                    sb.Append("|");
                    sb.Append(FreeFormString);
                    sb.Append("|");
                    sb.Append(string.Join(",", Filters.Select(x => x.ToQueryString())));
                    return sb.ToString();
                }
    
                public static InputQuery FromQueryString(string queryString)
                {
                    Match match = regex.Match(queryString);
                    InputQuery result = new InputQuery
                    {
                        StartAt = int.Parse(match.Groups["startat"].Value),
                        Count = int.Parse(match.Groups["count"].Value),
                        Language = match.Groups["language"].Value,
                        Engine = match.Groups["engine"].Value,
                        ContentSourceString = new List<string>(match.Groups["contentsourcestring"].Value.Split(',')),
                        FreeFormString = match.Groups["freeformstring"].Value,
                        Filters = new List<Filter>(match.Groups["filters"].Value.Split(',').Select(x => Filter.FromQueryString(x))),
                    };
    
                    return result;
                }
            }
    
            [DataContract]
            public class Filter
            {
                [DataMember]
                public string FilterString { get; set; }
                [DataMember]
                public string Operator { get; set; }
    
                public string ToQueryString()
                {
                    return string.Format("Filter-{0}-{1}", FilterString, Operator);
                }
    
                public static Filter FromQueryString(string queryString)
                {
                    Regex regex = new Regex(@"Filter\-([^\-]+)\-([^\-]+)");
                    Match match = regex.Match(queryString);
                    return new Filter { FilterString = match.Groups[1].Value, Operator = match.Groups[2].Value };
                }
            }
    
            public class MyQueryStringConverter : QueryStringConverter
            {
                public override bool CanConvert(Type type)
                {
                    return base.CanConvert(type) || type == typeof(InputQuery);
                }
    
                public override object ConvertStringToValue(string parameter, Type parameterType)
                {
                    if (parameterType == typeof(InputQuery))
                    {
                        return InputQuery.FromQueryString(parameter);
                    }
                    else
                    {
                        return base.ConvertStringToValue(parameter, parameterType);
                    }
                }
            }
    
            public class MyWebHttpBehavior : WebHttpBehavior
            {
                protected override QueryStringConverter GetQueryStringConverter(OperationDescription operationDescription)
                {
                    if (operationDescription.Messages[0].Body.Parts.Count == 1 &&
                        operationDescription.Messages[0].Body.Parts[0].Type == typeof(InputQuery))
                    {
                        return new MyQueryStringConverter();
                    }
                    else
                    {
                        return base.GetQueryStringConverter(operationDescription);
                    }
                }
            }
    
            public static void Test()
            {
                string baseAddress = "http://" + Environment.MachineName + ":8000/Service";
                ServiceHost host = new ServiceHost(typeof(Service), new Uri(baseAddress));
                ServiceEndpoint endpoint = host.AddServiceEndpoint(typeof(Service), new WebHttpBinding(), "");
                endpoint.Behaviors.Add(new MyWebHttpBehavior());
                host.Open();
                Console.WriteLine("Host opened");
    
                InputQuery input = new InputQuery
                {
                    ContentSourceString = new List<string> { "contentSource1", "contentSource2" },
                    Count = 100,
                    Engine = "engine",
                    Filters = new List<Filter>
                    {
                        new Filter { FilterString = "str1", Operator = "op1" },
                        new Filter { FilterString = "str2", Operator = "op2" },
                    },
                    FreeFormString = "freeFormString",
                    Language = "language",
                    StartAt = 20,
                };
    
                WebClient client = new WebClient();
                Console.WriteLine(client.DownloadString(baseAddress + "/Query?query=" + input.ToQueryString()));
    
                Console.Write("Press ENTER to close the host");
                Console.ReadLine();
                host.Close();
            }
        }
    
    

     


    Carlos Figueira
    Thursday, January 26, 2012 8:46 PM