开发者社区> 技术小牛人> 正文
阿里云
为了无法计算的价值
打开APP
阿里云APP内打开

实现在GET请求下调用WCF服务时传递对象(复合类型)参数

简介:
+关注继续查看

WCF实现RESETFUL架构很容易,说白了,就是使WCF能够响应HTTP请求并返回所需的资源,如果有人不知道如何实现WCF支持HTTP请求的,可参见我之前的文章《实现jquery.ajax及原生的XMLHttpRequest调用WCF服务的方法》、《实现jquery.ajax及原生的XMLHttpRequest跨域调用WCF服务的方法》,在此就不作重述。

实现WCF支持HTTP请求调用容易,但要实现类似MVC的ACTION及WEB API那样的灵活,那就得花费点功夫,为什么这样说呢?因为如果WCF的参数为普通类型(即:值类型),那么调用很容易,也支持HTTP的多种请求方法,比如常见的:GET,POST,例如:

1
2
3
4
5
6
7
[ServiceContract]
public interface IService1
{
              [OperationContract]
    [WebInvoke(Method = "*", UriTemplate = "Get/{value}", RequestFormat = WebMessageFormat.Json, ResponseFormat = WebMessageFormat.Json, BodyStyle = WebMessageBodyStyle.WrappedRequest)]
    string GetData(string value);
}
1
2
3
4
5
6
7
public class Service1 : IService1
{
    public string GetData(string value)
    {
        return string.Format("You entered: {0}", value);
    }
}

我们只需要通过浏览器访问如:http://localhost:14719/Service1.svc/Get/test   注意我加深的部份,需要与该服务方法上约定的UriTemplate相匹配,与MVC的ROUTE URL类似,POST也很简单,在此就不再说明。

上面的调用成功了,你是否就认为这样就完了呢?有没有想过如果WCF的服务方法参数为对象(复合类型),例如:

1
2
3
[OperationContract]
[WebInvoke(Method = "*",RequestFormat = WebMessageFormat.Json, ResponseFormat = WebMessageFormat.Json, BodyStyle = WebMessageBodyStyle.WrappedRequest)]
Point GetPoint(Point point);

Point定义:

1
2
3
4
5
6
7
8
9
10
11
12
[DataContract(Namespace="http://www.zuowenjun.cn/")]
public class Point
{
    [DataMember]
    public int X { getset; }
 
    [DataMember]
    public int Y { getset; }
 
    [DataMember]
    public string Value { getset; }
}
1
2
3
4
5
6
7
8
public Point GetPoint(Point point)
{
    if (point == null)
    {
        throw new ArgumentNullException("point");
    }
    return point;
}

你是否可以像上面那样来进行GET或POST请求呢?如果可以,那么该如何调用呢?本人(梦在旅途)是一个遇到了问题,就必需弄明白及必需解决的人,所以我进行了试验,首先试一下POST方法,示例代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
    <title></title>
    <script src="jquery-1.4.4.min.js" type="text/javascript"></script>
</head>
<body>
    X:<input id="Text1" type="text" />
    Y:<input id="Text2" type="text" />
    Value:<input id="Text3" type="text" />
    <input id="Button1" type="button" value="button" />
    <fieldset>
        <legend>结果输出:</legend>
        <div id="resultbox">
         
        </div>
    </fieldset>
    <script type="text/javascript">
        $(function () {
            $("#Button1").click(function () {
                $.ajax({
                    url: "Service1.svc/GetPoint",
                    contentType:"application/json",
                    type: "post",
                    datatype: "json",
                    data: JSON.stringify({ 'point': { 'X': $("#Text1").val(), 'Y': $("#Text2").val(), 'Value': $("#Text3").val()} }),
                    success: function (r,s,x) {
                        $("#resultbox").append("<p>" + JSON.stringify(r) + "</p>");
                    }
                });
            });
        });
    </script>
</body>
</html>

经调用成功,结果如下图示:

也可以参见DUDU的文章:jQuery调用WCF服务时如何传递对象参数

这里顺便提醒一下,在POST请求WCF服务方法参数为对象时,注意AJAX请求的Data必需是JSON字符串,不能是JSON对象,如果不明白两者的关系,我这里简要说明一下,

{X:1,Y:2,Value:'test'} 、{'X':1,'Y':2,'Value':'test'}--这些不论KEY加不加引号,都是JSON对象,可以通过JSON.stringify方法转换成JSON字符串

"{X:1,Y:2,Value:'test'}"、"{'X':1,'Y':2,'Value':'test'}"--这些就是JSON字符串,可以通过$.parseJSON方法转换成JSON对象

然后就试一下GET方法,在此我就有点难了,该如何进行GET请求呢?参数该如何对应呢?我将AJAX的TYPE改为GET,然后调用,最后结果返回如下图:

一看就知道,调用失败,无法为WCF的服务方法参数point赋值,也就是WCF无法正确将请求的参数解析成POINT类型对象实例,我尝试着直接在浏览器中访问:

http://localhost:14719/Service1.svc/GetPoint?point={x=1&y=2&value=test},结果仍是报同样的错误,这个问题却是困扰了我几天,我也曾请教过DUDU,QQ群,博问:http://q.cnblogs.com/q/77775/,都没有人能回答我,有人说复合类型不支持GET,用不了GET就不要用之类的话,我就想,我的服务契约上定义的是Method = "*",而这里GET却不行,难道不能用*,我不放弃,经过多方查证,这篇文章给了我明确的思路:http://www.cnblogs.com/huangxincheng/p/4621971.html,可以通过自定义MessageFormatter来实现自己想要的解析,好了废话不多说,直接上代码。

自定义PointMessageFormatter:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
public class PointMessageFormatter:IDispatchMessageFormatter
{
    private IDispatchMessageFormatter innerFormatter;
 
    public PointMessageFormatter(IDispatchMessageFormatter innerFormatter)
    {
        this.innerFormatter = innerFormatter;
    }
 
    public void DeserializeRequest(System.ServiceModel.Channels.Message message, object[] parameters)
    {
        innerFormatter.DeserializeRequest(message, parameters);
 
        if (message.Properties["HttpOperationName"].ToString().Equals("GetPoint",StringComparison.OrdinalIgnoreCase) && parameters.Count() > 0 && parameters[0] == null)
        {
            var request = message.Properties.Values.ElementAt(1) as HttpRequestMessageProperty;
            if (request.Method.Equals("GET", StringComparison.OrdinalIgnoreCase))
            {
                parameters[0] = DeserializeFromQueryString(request.QueryString);
            }
        }
         
    }
 
    private object DeserializeFromQueryString(string queryString)
    {
        if (string.IsNullOrEmpty(queryString)) return null;
        var t=typeof(Point);
        var point = new Point();
        foreach (var in t.GetProperties(BindingFlags.Public | BindingFlags.Instance))
        {
            Regex regx = new Regex(string.Format(@"(?<={0}=)(.*?)(?=\&|$)", p.Name), RegexOptions.IgnoreCase);
            string value = regx.Match(queryString).Groups[1].Value;
            try
            {
                var pValue = Convert.ChangeType(value,p.PropertyType);
                p.SetValue(point, pValue, null);
            }
            catch
            { }
 
        }
        return point;
    }
 
    public System.ServiceModel.Channels.Message SerializeReply(MessageVersion messageVersion, object[] parameters, object result)
    {
        return innerFormatter.SerializeReply(messageVersion, parameters, result);
    }
}

自定义MyOperationBehavior:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
public class MyOperationBehavior : Attribute, IOperationBehavior
{
    public int MaxLength { getset; }
 
    public void AddBindingParameters(OperationDescription operationDescription, BindingParameterCollection bindingParameters)
    {
 
    }
 
    public void ApplyClientBehavior(OperationDescription operationDescription, ClientOperation clientOperation)
    {
 
    }
 
    public void ApplyDispatchBehavior(OperationDescription operationDescription, DispatchOperation dispatchOperation)
    {
        dispatchOperation.Formatter = new PointMessageFormatter(dispatchOperation.Formatter);
    }
 
    public void Validate(OperationDescription operationDescription)
    {
 
    }
}

然后修改GetPoint服务方法实现:

1
2
3
4
5
6
7
8
9
[MyOperationBehavior]
public Point GetPoint(Point point)
{
    if (point == null)
    {
        throw new ArgumentNullException("point");
    }
    return point;
}

其实方法实现内容没有变,就是在其方法上增加了一个特性:MyOperationBehavior,目的是:当请求调用该方法时,会使用我上面定义的PointMessageFormatter,从而实现了自定义解析。

最后我们再试验通过GET请求,这里我就直接通过浏览器访问:http://localhost:14719/Service1.svc/GetPoint?x=12&y=232&value=test,最后返回的结果如下:

可以看出,调用成功,已经能正常解析到我的参数了,当然我上面的实现可能比较粗糙,也有很多的限制,但我这里只是提供一种思路,一种解决方案,大家可以基于此思路实现更牛B的功能。其实WCF的可扩展的地方非常多,可以参见artech大牛关于WCF的系列文章,我感觉他的文章写得比较深入,但就是不易懂。这篇文章我也是花了很多精力来思考与解决,希望能帮助到大家,如果觉得不错,给个推荐吧,谢谢!

本文转自 梦在旅途 博客园博客,原文链接:http://www.cnblogs.com/zuowj/p/5017154.html  ,如需转载请自行联系原作者

版权声明:本文内容由阿里云实名注册用户自发贡献,版权归原作者所有,阿里云开发者社区不拥有其著作权,亦不承担相应法律责任。具体规则请查看《阿里云开发者社区用户服务协议》和《阿里云开发者社区知识产权保护指引》。如果您发现本社区中有涉嫌抄袭的内容,填写侵权投诉表单进行举报,一经查实,本社区将立刻删除涉嫌侵权内容。

相关文章
SAP S/4HANA Analytics Path Framework 里过滤器(filter)的使用方法介绍
SAP S/4HANA Analytics Path Framework 里过滤器(filter)的使用方法介绍
65 0
【云计算的1024种玩法】云监控的深入使用和云监控钉钉机器人创建
网站监控是网站管理中非常重要的一个环节,很多小微企业的时候网站无法打开、服务宕机的时候基本上都不是访客因为无法访问网站发的邮件或者通过即时通信找的,这就显得很尴尬了。 所以我们需要使用一款企业级的监控工具来检测我们的网站,而阿里云·云监控就是这么一款非常好用的企业级监控服务工具,而且还是免费的...
2769 0
ESS 控制台发布新功能:创建多可用区专有网络伸缩组、支持伸缩组内 SLB 挂载不同网络类型 ECS 实例
ESS 伸缩组是具有相同应用场景的 ECS 实例的集合,为了更好地提供弹性能力,ESS 控制台新发布了两个功能:ESS 全面支持创建多可用区专有网络伸缩组功能;ESS 全面支持伸缩组内 SLB 挂载不同网络类型的 ECS 实例功能。
5719 0
使用CSS预处理器Less
前天写了一篇文章,关于如何使用Harp来加快人的开发效率,在Mac系统和Linux系统上测试是没有问题的,但没有在Windows上测试,使用Windows的刚入门的前端工程师,安装Harp失败后,不知道怎么解决问题。
836 0
【Web API系列教程】3.5 — 实战:处理数据(创建数据传输对象)
现在,我们的Web API暴露数据库实体给客户端,而客户端接收直接映射到你的数据库表的数据。然而,这不永远都是个好办法。有时候你可以想要改变发送到客户端的数据的形式。
859 0
使用WPF教你一步一步实现连连看(三)
这次首先对以前的结构进行了调整: 第一步:把MyButton按钮的属性独立成一个类,放在一个单独的MyButton.cs中,把图片的初始化也放到里面。 调整代码如下:   public class MyButton : Button     {         //图...
929 0
5723
文章
0
问答
文章排行榜
最热
最新
相关电子书
更多
低代码开发师(初级)实战教程
立即下载
阿里巴巴DevOps 最佳实践手册
立即下载
冬季实战营第三期:MySQL数据库进阶实战
立即下载