RESTful WCF web service POST problem

懵懂的女人 提交于 2019-12-04 10:50:40

WCF doesn't support forms/encoded data out-of-the box. The other answers mentioned some alternatives (receive the input as a Stream, change the request to JSON). Another alternative, which doesn't force you to change the request or the operation is to use a custom formatter which understands form-urlencoded requests. The code below shows one which does that.

public class MyWebHttpBehavior : WebHttpBehavior
{
    protected override IDispatchMessageFormatter GetRequestDispatchFormatter(OperationDescription operationDescription, ServiceEndpoint endpoint)
    {
        bool isRequestWrapped = this.IsRequestWrapped(operationDescription.Behaviors.Find<WebInvokeAttribute>());
        IDispatchMessageFormatter originalFormatter = base.GetRequestDispatchFormatter(operationDescription, endpoint);
        if (isRequestWrapped)
        {
            return new MyFormUrlEncodedAwareFormatter(
                operationDescription,
                originalFormatter,
                this.GetQueryStringConverter(operationDescription));
        }
        else
        {
            return originalFormatter;
        }
    }

    private bool IsRequestWrapped(WebInvokeAttribute wia)
    {
        WebMessageBodyStyle bodyStyle;
        if (wia.IsBodyStyleSetExplicitly)
        {
            bodyStyle = wia.BodyStyle;
        }
        else
        {
            bodyStyle = this.DefaultBodyStyle;
        }

        return bodyStyle == WebMessageBodyStyle.Wrapped || bodyStyle == WebMessageBodyStyle.WrappedRequest;
    }

    class MyFormUrlEncodedAwareFormatter : IDispatchMessageFormatter
    {
        const string FormUrlEncodedContentType = "application/x-www-form-urlencoded";
        OperationDescription operation;
        IDispatchMessageFormatter originalFormatter;
        QueryStringConverter queryStringConverter;
        public MyFormUrlEncodedAwareFormatter(OperationDescription operation, IDispatchMessageFormatter originalFormatter, QueryStringConverter queryStringConverter)
        {
            this.operation = operation;
            this.originalFormatter = originalFormatter;
            this.queryStringConverter = queryStringConverter;
        }

        public void DeserializeRequest(Message message, object[] parameters)
        {
            if (IsFormUrlEncodedMessage(message))
            {
                XmlDictionaryReader bodyReader = message.GetReaderAtBodyContents();
                bodyReader.ReadStartElement("Binary");
                byte[] bodyBytes = bodyReader.ReadContentAsBase64();
                string body = Encoding.UTF8.GetString(bodyBytes);
                NameValueCollection pairs = HttpUtility.ParseQueryString(body);
                Dictionary<string, string> values = new Dictionary<string, string>();
                foreach (var key in pairs.AllKeys)
                {
                    values.Add(key, pairs[key]);
                }

                foreach (var part in this.operation.Messages[0].Body.Parts)
                {
                    if (values.ContainsKey(part.Name))
                    {
                        string value = values[part.Name];
                        parameters[part.Index] = this.queryStringConverter.ConvertStringToValue(value, part.Type);
                    }
                    else
                    {
                        parameters[part.Index] = GetDefaultValue(part.Type);
                    }
                }
            }
            else
            {
                this.originalFormatter.DeserializeRequest(message, parameters);
            }
        }

        public Message SerializeReply(MessageVersion messageVersion, object[] parameters, object result)
        {
            throw new NotSupportedException("This is a request-only formatter");
        }

        private static bool IsFormUrlEncodedMessage(Message message)
        {
            object prop;
            if (message.Properties.TryGetValue(WebBodyFormatMessageProperty.Name, out prop))
            {
                if (((WebBodyFormatMessageProperty)prop).Format == WebContentFormat.Raw)
                {
                    if (message.Properties.TryGetValue(HttpRequestMessageProperty.Name, out prop))
                    {
                        if (((HttpRequestMessageProperty)prop).Headers[HttpRequestHeader.ContentType].StartsWith(FormUrlEncodedContentType))
                        {
                            return true;
                        }
                    }
                }
            }

            return false;
        }

        private static object GetDefaultValue(Type type)
        {
            if (type.IsValueType)
            {
                return Activator.CreateInstance(type);
            }
            else
            {
                return null;
            }
        }
    }
}
[ServiceContract]
public class Service
{
    [WebInvoke(BodyStyle = WebMessageBodyStyle.WrappedRequest)]
    public string Concat(string text1, string text2)
    {
        return text1 + text2;
    }

    [WebInvoke(BodyStyle = WebMessageBodyStyle.WrappedRequest)]
    public int Add(int x, int y)
    {
        return x + y;
    }
}
class Program
{
    public static void SendRequest(string uri, string method, string contentType, string body)
    {
        HttpWebRequest req = (HttpWebRequest)HttpWebRequest.Create(uri);
        req.Method = method;
        if (!String.IsNullOrEmpty(contentType))
        {
            req.ContentType = contentType;
        }

        if (body != null)
        {
            byte[] bodyBytes = Encoding.UTF8.GetBytes(body);
            req.GetRequestStream().Write(bodyBytes, 0, bodyBytes.Length);
            req.GetRequestStream().Close();
        }

        HttpWebResponse resp;
        try
        {
            resp = (HttpWebResponse)req.GetResponse();
        }
        catch (WebException e)
        {
            resp = (HttpWebResponse)e.Response;
        }
        Console.WriteLine("HTTP/{0} {1} {2}", resp.ProtocolVersion, (int)resp.StatusCode, resp.StatusDescription);
        foreach (string headerName in resp.Headers.AllKeys)
        {
            Console.WriteLine("{0}: {1}", headerName, resp.Headers[headerName]);
        }
        Console.WriteLine();
        Console.WriteLine(new StreamReader(resp.GetResponseStream()).ReadToEnd());
        Console.WriteLine();
        Console.WriteLine("  *-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*  ");
        Console.WriteLine();
    }

    static void Main(string[] args)
    {
        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");

        SendRequest(baseAddress + "/Add", "POST", "application/json", "{\"x\":22,\"y\":33}");
        SendRequest(baseAddress + "/Add", "POST", "application/x-www-form-urlencoded", "x=22&y=33");
        SendRequest(baseAddress + "/Add", "POST", "application/json", "{\"x\":22,\"z\":33}");
        SendRequest(baseAddress + "/Add", "POST", "application/x-www-form-urlencoded", "x=22&z=33");

        SendRequest(baseAddress + "/Concat", "POST", "application/json", "{\"text1\":\"hello\",\"text2\":\" world\"}");
        SendRequest(baseAddress + "/Concat", "POST", "application/x-www-form-urlencoded", "text1=hello&text2=%20world");
        SendRequest(baseAddress + "/Concat", "POST", "application/json", "{\"text1\":\"hello\",\"text9\":\" world\"}");
        SendRequest(baseAddress + "/Concat", "POST", "application/x-www-form-urlencoded", "text1=hello&text9=%20world");
    }
}

It looks like you need to parse your post data manual...

You can look here for example

The only thing that looks out of place is the playerJson2 but that is just because I have never used the UriTemplate before. Can you get it to work without the UriTemplate and just post to /WcfService1/Service1.svc/GetPlayers? Are there any other WCF services that you have working in the project?

You need to set the correct Content-Type which is application/json in this case.

Sorry for misreading your question. Update your UriTemplate as follows:

[WebInvoke(Method = "POST",
       ResponseFormat = WebMessageFormat.Json,
       UriTemplate = "playersJson2?name1={name1}&name2={name2}")]    
标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!