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

本文涉及的产品
全局流量管理 GTM,标准版 1个月
云解析 DNS,旗舰版 1个月
公共DNS(含HTTPDNS解析),每月1000万次HTTP解析
简介:

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 {  get set ; }
 
     [DataMember]
     public  int  Y {  get set ; }
 
     [DataMember]
     public  string  Value {  get set ; }
}
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 {  get set ; }
 
     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  ,如需转载请自行联系原作者

相关文章
|
前端开发
WCF更新服务引用报错的原因之一
WCF更新服务引用报错的原因之一
|
C# 数据安全/隐私保护
c#如何创建WCF服务到发布(SqlServer版已经验证)
c#如何创建WCF服务到发布(SqlServer版已经验证)
67 0
|
安全 数据库连接 数据库
WCF服务创建到发布(SqlServer版)
在本示例开始之前,让我们先来了解一下什么是wcf? wcf有哪些特点? wcf是一个面向服务编程的综合分层架构。该架构的项层为服务模型层。 使用户用最少的时间和精力建立自己的软件产品和外界通信的模型。它使得开发者能够建立一个跨平台的安全、可信赖、事务性的解决方案。且能与已有系统兼容写作。 简单概括就是:一组数据通信的应用程序开发接口。
98 0
|
C++
WCF基础教程(二)——解析iis8和iis8.5+VS2013发布wcf服务问题
WCF基础教程(二)——解析iis8和iis8.5+VS2013发布wcf服务问题
132 0
WCF基础教程(二)——解析iis8和iis8.5+VS2013发布wcf服务问题
WCF使用纯代码的方式进行服务寄宿
服务寄宿的目的是为了开启一个进程,为WCF服务提供一个运行的环境。通过为服务添加一个或者多个终结点,使之暴露给潜在的服务消费,服务消费者通过匹配的终结点对该服务进行调用,除去上面的两种寄宿方式,还可以以纯代码的方式实现服务的寄宿工作。
884 0
|
Windows
WCF服务寄宿到IIS
一.WCF简介: Windows Communication Foundation(WCF)是由微软开发的一系列支持数据通信的应用程序框架,可以翻译为Windows 通讯开发平台。整合了原有的windows通讯的 .net Remoting,WebService,Socket的机制,并融合有HTTP和FTP的相关技术。
1088 0
WCF服务自我寄宿
WCF服务的寄宿方式 WCF寄宿方式是一种非常灵活的操作,可以寄宿在各种进程之中,常见的寄宿有: IIS服务、Windows服务、Winform程序、控制台程序中进行寄宿,从而实现WCF服务的运行,为调用者方便、高效提供服务调用。
1027 0
|
网络架构
(纯代码)快速创建wcf rest 服务
因为有一个小工具需要和其它的业务对接数据,所以就试一下看能不能弄一个无需配置快速对接的方法出来,百(以)度(讹)过(传)后(讹),最后还是对照wcf配置对象调试出来了: 1.创建WebHttpBinding 2.
1006 0