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
p
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 ,如需转载请自行联系原作者