如何定义NET 4 REST WCF服务以接受url编码的字符串参数

时间:2012-02-05 23:00:50

标签: wcf rest post urlencode

我在NET4中有一个WCF REST服务定义如下:

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

我需要通过POST从客户端调用它,将数据发布为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);
    }

我应该如何定义UriTemplate,或者我应该添加哪些其他属性以允许服务接受上面显示的客户端代码发布的参数?

谢谢

1 个答案:

答案 0 :(得分:4)

您可以使用知道如何处理此格式的自定义消息格式化程序,因为WCF不支持开箱即用,如下面的代码所示。您还可以查看codeplex(http://wcf.codeplex.com)中的Web API项目,该项目本身支持此格式。

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();
    }
}