【问题标题】:RESTful WCF web service POST problemRESTful WCF web 服务 POST 问题
【发布时间】:2011-09-30 02:35:22
【问题描述】:

我无法将参数传递给 wcf Web 服务。我的网络方法:

    [OperationContract]
    [WebInvoke(Method = "POST",
       ResponseFormat = WebMessageFormat.Json,
       UriTemplate = "playersJson2")]
    List<Person> GetPlayers(string name1, string name2);

当我发出 http post 请求时,我得到了正确的 json 格式的 200 OK 响应,但 Web 服务似乎无法获取参数(name1,name2)。 Wireshark 显示如下:

你看出什么不对了吗?

更新:不确定这是否重要,但我的服务正在使用“webHttpBinding”并且发布请求来自 Android。

【问题讨论】:

    标签: c# wcf http-post


    【解决方案1】:

    WCF 不支持开箱即用的表单/编码数据。其他答案提到了一些替代方案(将输入作为流接收,将请求更改为 JSON)。另一种不强制您更改请求或操作的替代方法是使用自定义格式化程序,该格式化程序可以理解 form-urlencoded 请求。下面的代码显示了一个这样做的。

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

    【讨论】:

    • 即使我没有尝试你的代码,我认为你是对的,WCF 默认不支持非 XML/JSON 的东西。我会尝试以 json 形式发送请求。谢谢
    【解决方案2】:

    看来您需要解析您的发布数据手册...

    例如你可以看here

    【讨论】:

    • 感谢您的链接。似乎很有帮助。可能是 url 编码实体的问题,还在寻找问题所在。
    【解决方案3】:

    唯一看起来不合适的是playerJson2,但这只是因为我以前从未使用过 UriTemplate。你能在没有 UriTemplate 的情况下让它工作并发布到/WcfService1/Service1.svc/GetPlayers 吗?您在项目中还有其他 WCF 服务吗?

    【讨论】:

    • 抱歉,这是我的错字,我还有其他 GET 方法可以正常工作。
    【解决方案4】:

    您需要设置正确的Content-Type,在这种情况下为application/json

    很抱歉误读了您的问题。如下更新您的 UriTemplate:

    [WebInvoke(Method = "POST",
           ResponseFormat = WebMessageFormat.Json,
           UriTemplate = "playersJson2?name1={name1}&name2={name2}")]    
    

    【讨论】:

    • 但是请求内容类型不是 JSON; JSON 将是 {"name1":"Bob","name2":"Joanne"} 而不是 name1=Bob&amp;name2=Joanne1
    • application/json 将是 WCF 服务响应的内容类型,而不是请求的内容类型
    • 那是另一个问题。查询参数不能反序列化为 JSON 对象(不是不使用一些技巧)。
    • @Alexander,要将 JSON 发送到 WCF 服务,内容类型应为 application/json
    • @Mrchief,你说得对,但问题似乎是如何发送 urlencoded 数据
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-02-18
    • 1970-01-01
    • 1970-01-01
    • 2011-05-30
    相关资源
    最近更新 更多