1

I have a WCF REST service in NET4 defined as below:

[WebInvoke(UriTemplate = "/", Method = "POST")]
public Stream ProcessPost(string p1, string p2, string p3, string p4)
{
    return Execute(p1, p2, p3, p4);
}

I need to call it from clients with POST, that post data as form-urlencoded

    HttpWebRequest req = WebRequest.Create(url) as HttpWebRequest;
    req.Method = "POST";
    req.ContentType = "application/x-www-form-urlencoded";

    string paramz = string.Format("p1={0}&p2={1}&p3={2}&p4={3}", 
        HttpUtility.UrlEncode("str1"),
        HttpUtility.UrlEncode("str2"),
        HttpUtility.UrlEncode("str3"),
        HttpUtility.UrlEncode("str4")
        );

    // Encode the parameters as form data:
    byte[] formData =
        UTF8Encoding.UTF8.GetBytes(paramz);
    req.ContentLength = postData.Length;

    // Send the request:
    using (Stream post = req.GetRequestStream())
    {
        post.Write(formData, 0, formData.Length);
    }

How should I define UriTemplate, or what other attributes should I add to allow the service to accept the parameters as posted by the client code shown above?

Thanks

1 Answer 1

4

You can use a custom message formatter which knows how to deal with this format, since it's not supported out-of-the-box by WCF, as shown in the code below. You can also take a look at the Web API project in codeplex (http://wcf.codeplex.com), which has support for this format natively.

public class StackOverflow_9153898
{
    [ServiceContract]
    public class Service
    {
        [WebInvoke(UriTemplate = "/", Method = "POST", BodyStyle = WebMessageBodyStyle.WrappedRequest)]
        public Stream ProcessPost(string p1, string p2, string p3, string p4)
        {
            return Execute(p1, p2, p3, p4);
        }

        private Stream Execute(string p1, string p2, string p3, string p4)
        {
            WebOperationContext.Current.OutgoingResponse.ContentType = "text/plain";
            string response = p1 + "-" + p2 + "-" + p3 + "-" + p4;
            return new MemoryStream(Encoding.UTF8.GetBytes(response));
        }
    }

    public class MyFormsUrlEncodedBehavior : IEndpointBehavior
    {
        public void AddBindingParameters(ServiceEndpoint endpoint, BindingParameterCollection bindingParameters)
        {
        }

        public void ApplyClientBehavior(ServiceEndpoint endpoint, ClientRuntime clientRuntime)
        {
        }

        public void ApplyDispatchBehavior(ServiceEndpoint endpoint, EndpointDispatcher endpointDispatcher)
        {
            foreach (OperationDescription operationDescription in endpoint.Contract.Operations)
            {
                var dispatchOperation = endpointDispatcher.DispatchRuntime.Operations[operationDescription.Name];
                dispatchOperation.Formatter = new MyFormsUrlEncodedDispatchFormatter(operationDescription, dispatchOperation.Formatter);
            }
        }

        public void Validate(ServiceEndpoint endpoint)
        {
        }
    }

    class MyFormsUrlEncodedDispatchFormatter : IDispatchMessageFormatter
    {
        OperationDescription operation;
        IDispatchMessageFormatter originalFormatter;

        public MyFormsUrlEncodedDispatchFormatter(OperationDescription operation, IDispatchMessageFormatter originalFormatter)
        {
            this.operation = operation;
            this.originalFormatter = originalFormatter;
        }

        public void DeserializeRequest(Message message, object[] parameters)
        {
            var reqProp = message.Properties[HttpRequestMessageProperty.Name] as HttpRequestMessageProperty;
            if (reqProp.Headers[HttpRequestHeader.ContentType] == "application/x-www-form-urlencoded")
            {
                var bodyReader = message.GetReaderAtBodyContents();
                var bodyBytes = bodyReader.ReadElementContentAsBase64();
                var body = Encoding.UTF8.GetString(bodyBytes);
                NameValueCollection pairs = HttpUtility.ParseQueryString(body);
                DeserializeParameters(pairs, parameters);
                return;
            }

            this.originalFormatter.DeserializeRequest(message, parameters);
        }

        private void DeserializeParameters(NameValueCollection pairs, object[] parameters)
        {
            foreach (var part in this.operation.Messages[0].Body.Parts)
            {
                string name = part.Name;
                string value = pairs[name];
                switch (Type.GetTypeCode(part.Type))
                {
                    case TypeCode.Boolean:
                        parameters[part.Index] = Convert.ToBoolean(value);
                        break;
                    case TypeCode.Byte:
                        parameters[part.Index] = Convert.ToByte(value);
                        break;
                    case TypeCode.Char:
                        parameters[part.Index] = Convert.ToChar(value);
                        break;
                    // Skipped many others
                    case TypeCode.String:
                        parameters[part.Index] = value;
                        break;
                    default:
                        throw new NotImplementedException("Not implemented for type " + part.Type);
                }
            }
        }

        public Message SerializeReply(MessageVersion messageVersion, object[] parameters, object result)
        {
            return this.originalFormatter.SerializeReply(messageVersion, parameters, result);
        }
    }

    public static void Test()
    {
        string baseAddress = "http://" + Environment.MachineName + ":8000/Service";
        ServiceHost host = new WebServiceHost(typeof(Service), new Uri(baseAddress));
        ServiceEndpoint endpoint = host.AddServiceEndpoint(typeof(Service), new WebHttpBinding(), "");
        endpoint.Behaviors.Add(new WebHttpBehavior());

        // this needs to go *after* the WebHttpBehavior
        endpoint.Behaviors.Add(new MyFormsUrlEncodedBehavior());
        host.Open();
        Console.WriteLine("Host opened");

        WebClient c = new WebClient();
        c.Headers[HttpRequestHeader.ContentType] = "application/x-www-form-urlencoded";
        Console.WriteLine(c.UploadString(baseAddress + "/", "p1=str1&p2=str2&p3=str3&p4=str4"));

        Console.Write("Press ENTER to close the host");
        Console.ReadLine();
        host.Close();
    }
}
Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.