10
votes

I have an AJAX call that I want to run against a WCF GET service. Basically, the call to the service (via jquery) looks like this:

$.get(serviceEndpoint, {query : "some search text", statusTypes: [1, 2]}, function (result) { /* do something*/ }, 'text');

When this call gets run, I see the GET in firebug go through correctly, and I do hit the endpoint. However, the parameter statusTypes is always null.

The GET itself from jquery looks like it is encoded, but when I don't encode the brackets, the call won't enter the endpoint at all:

http://localhost/Services/SomeService.svc/Endpoint?statusTypes%5B%5D=1&statusTypes%5B%5D=2&query=some+search+text

And the WCF service itself:

[OperationContract]

[WebInvoke(Method= "GET", BodyStyle = WebMessageBodyStyle.WrappedRequest,
ResponseFormat = WebMessageFormat.Json)]

public ResultsViewModel GetTags(string query, int[] statusTypes)

Is it possible to pass an array via GET to a WCF service?

The permutations aren't numerous, so I could write an individual endpoint "per array", but I'd rather keep it in one.

2
As a workaround, you convert your array to a string and then parse it in your service using String.Split().Zachary

2 Answers

10
votes

It is possible, but not with the out-of-the-box WCF. With the "jQuery support" in the WCF codeplex page, you can receive all of the data sent by jQuery (including arrays, nested objects, etc) in an untyped variable, both on the body (for POST requests) and on the query string (for GET). The mapping between jQuery array variables (whose names contain '[' and ']') and operation parameters cannot be done in WCF 4.0 (at least not without writing a message formatter).

This should be simpler, however, on the new WCF Web APIs (also available on the codeplex site).

Update: this is an example of a formatter which works for your scenario:

public class StackOverflow_6445171
{
    [ServiceContract]
    public class Service
    {
        [WebGet(ResponseFormat = WebMessageFormat.Json)]
        public string GetLabelPacketTags(string query, int[] statusTypes)
        {
            StringBuilder sb = new StringBuilder();
            sb.Append("Query=" + query);
            sb.Append(", statusTypes=");
            if (statusTypes == null)
            {
                sb.Append("null");
            }
            else
            {
                sb.Append("[");
                for (int i = 0; i < statusTypes.Length; i++)
                {
                    if (i > 0) sb.Append(",");
                    sb.Append(statusTypes[i]);
                }
                sb.Append("]");
            }

            return sb.ToString();
        }
    }
    class MyWebHttpBehavior : WebHttpBehavior
    {
        protected override IDispatchMessageFormatter GetRequestDispatchFormatter(OperationDescription operationDescription, ServiceEndpoint endpoint)
        {
            return new MyArrayAwareFormatter(operationDescription, this.GetQueryStringConverter(operationDescription));
        }

        class MyArrayAwareFormatter : IDispatchMessageFormatter
        {
            OperationDescription operation;
            QueryStringConverter queryStringConverter;
            public MyArrayAwareFormatter(OperationDescription operation, QueryStringConverter queryStringConverter)
            {
                this.operation = operation;
                this.queryStringConverter = queryStringConverter;
            }

            public void DeserializeRequest(Message message, object[] parameters)
            {
                if (message.Properties.ContainsKey("UriMatched") && (bool)message.Properties["UriMatched"])
                {
                    UriTemplateMatch match = message.Properties["UriTemplateMatchResults"] as UriTemplateMatch;
                    NameValueCollection queryValues = match.QueryParameters;
                    foreach (MessagePartDescription parameterDescr in this.operation.Messages[0].Body.Parts)
                    {
                        string parameterName = parameterDescr.Name;
                        int index = parameterDescr.Index;
                        if (parameterDescr.Type.IsArray)
                        {
                            Type elementType = parameterDescr.Type.GetElementType();
                            string[] values = queryValues.GetValues(parameterName + "[]");
                            Array array = Array.CreateInstance(elementType, values.Length);
                            for (int i = 0; i < values.Length; i++)
                            {
                                array.SetValue(this.queryStringConverter.ConvertStringToValue(values[i], elementType), i);
                            }
                            parameters[index] = array;
                        }
                        else
                        {
                            parameters[index] = this.queryStringConverter.ConvertStringToValue(queryValues.GetValues(parameterName)[0], parameterDescr.Type);
                        }
                    }
                }
            }

            public Message SerializeReply(MessageVersion messageVersion, object[] parameters, object result)
            {
                throw new NotSupportedException("This is a request-only formatter");
            }
        }
    }
    public static void Test()
    {
        string baseAddress = "http://" + Environment.MachineName + ":8000/Service";
        ServiceHost host = new ServiceHost(typeof(Service), new Uri(baseAddress));
        host.AddServiceEndpoint(typeof(Service), new WebHttpBinding(), "").Behaviors.Add(new MyWebHttpBehavior());
        host.Open();
        Console.WriteLine("Host opened");

        WebClient c = new WebClient();
        Console.WriteLine(c.DownloadString(baseAddress + "/GetLabelPacketTags?query=some+text&statusTypes[]=1&statusTypes[]=2"));
        Console.WriteLine(c.DownloadString(baseAddress + "/GetLabelPacketTags?query=some+text&statusTypes%5B%5D=1&statusTypes%5B%5D=2"));

        Console.Write("Press ENTER to close the host");
        Console.ReadLine();
        host.Close();
    }
}
2
votes

No it is not possible. You cannot map array of values from URL to parameter. If you want to pass array use HTTP POST.