通过 GET 将数组传递给 WCF 服务

Posted

技术标签:

【中文标题】通过 GET 将数组传递给 WCF 服务【英文标题】:Passing an array to WCF service via GET 【发布时间】:2011-06-22 19:07:19 【问题描述】:

我有一个要针对 WCF GET 服务运行的 AJAX 调用。基本上,对服务的调用(通过 jquery)如下所示:

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

当此调用运行时,我看到 firebug 中的 GET 正确通过,并且确实到达了端点。但是,参数statusTypes 始终为空。

来自 jquery 的 GET 本身看起来像是被编码的,但是当我不编码括号时,调用根本不会进入端点:

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

还有 WCF 服务本身:

[运营合同]

[WebInvoke(Method="GET", BodyStyle = WebMessageBodyStyle.WrappedRequest, 响应格式 = WebMessageFormat.Json)]

公开 结果视图模型 GetTags(字符串查询,int[] 状态类型)

是否可以?

排列并不多,因此我可以“每个数组”编写一个单独的端点,但我宁愿将它保留在一个中。

【问题讨论】:

作为一种解决方法,您可以将数组转换为字符串,然后使用 String.Split() 在您的服务中解析它。 【参考方案1】:

这是可能的,但不能使用开箱即用的 WCF。使用WCF codeplex page 中的“jQuery 支持”,您可以在正文(对于 POST 请求)和查询字符串中以无类型变量接收 jQuery 发送的所有数据(包括数组、嵌套对象等) (忘记)。 jQuery 数组变量(其名称包含 '[' 和 ']')和操作参数之间的映射不能在 WCF 4.0 中完成(至少在不编写消息格式化程序的情况下不能)。

不过,在新的 WCF Web API(也可在 codeplex 网站上获得)上,这应该更简单。

更新:这是适用于您的场景的格式化程序示例:

public class ***_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();
    

【讨论】:

如何通过 app.config 而不是代码注册自定义行为类? 自己找到答案 - 需要从自定义行为扩展中返回它:codeproject.com/Articles/37156/… 并将其添加到 web.config 中的行为扩展中【参考方案2】:

不,这是不可能的。您不能将值数组从 URL 映射到参数。如果要传递数组,请使用 HTTP POST。

【讨论】:

你实际上可以(见其他答案),只是不容易做到。 @carlos:是的,我明白了。非常好的答案和大量的工作使它工作。我的回答更针对当前 UriTemplate 开箱即用的支持。我没有考虑过扩展。您还可以将原始请求捕获为流并尝试提取查询字符串。

以上是关于通过 GET 将数组传递给 WCF 服务的主要内容,如果未能解决你的问题,请参考以下文章

将数组从 MS Flow 传递到 WCF 服务

如何将(声明)安全令牌传递给启用 WIF 的 WCF 服务?

将 Windows 用户“令牌”传递给 WCF 进行身份验证

将单个枚举值传递给 WCF 服务时,protobuf-net 引发有关无效线路类型的异常

WCF如何传递令牌进行身份验证?

让 DefaultNetworkCredentials 传递给 WCF 服务