POST调用WCF方法-项目实践
做即时通信项目时,需要与oa系统对接接口,主要目标是实现在oa里进行一项事项,通过调用我们的接口,即时通知过来,并弹出消息框提示一下。我们的即时通信使用的wcf服务进行通信,在客户端调用通信时,用的就是直接添加服务引用的方式,无可厚非,但是oa系统对接了太多项目,不能使用添加服务引用的方式,问题就来了,不得已只能更改我们的接口。
因为不熟悉这一块,浪费了不少时间,总结一下过程,方便以后使用。
微软官方文档:https://msdn.microsoft.com/zh-cn/library/dd315413.aspx#id0070003
一、修改
首先,说一下用到的技术。rest编程模型,为了能够让oa这边用他们通用的方法直接调用api,上网查找post调用wcf的方法,我见识浅薄,只知道使用rest编程的方式。
1.修改接口
主要是添加一个特性【webinvoke】,该属性指示服务操作在逻辑上就是调用操作,且可由wcf rest编程模型调用。
这里可以指定是get调用或者post调用,一般的格式如下:
get:
[operationcontract]
[webinvoke(method = "get", responseformat = webmessageformat.json, uritemplate = "say")]
string say();
[operationcontract]
[webinvoke(method = "get", responseformat = webmessageformat.json, uritemplate = "sayhello/{value}/{a}")]
string sayhello(string value, string a);
post:
[operationcontract]
[webinvoke(bodystyle = webmessagebodystyle.wrapped, responseformat = webmessageformat.json, requestformat = webmessageformat.json, method = "post")]
string postdatatest(string value,string content);
[operationcontract]
[webinvoke(bodystyle = webmessagebodystyle.wrapped,responseformat = webmessageformat.json, requestformat = webmessageformat.json, method = "post")]
void worksend(string title, string receiverid, string senderid, string content, string url, string msgtype);
推荐一个网址,里面有rest的介绍与详细的步骤:
http://www.cnblogs.com/wuhong/archive/2011/01/13/1934492.html(方法的参数是类对象,我这里用参数就可以实现)
2.修改服务端app.config/web.config
需要完全修改原来提供的接口配置。
首先在<bindings>标签内添加webhttpbinding,简单描述一下这种绑定协议:
webhttpbinding允许开发人员通过 http 请求(这些请求使用“plain old xml”(pox) 样式消息,而不是使用基于 soap 的消息)来公开 web 服务,可以很便利的实现rest。
与其他绑定不同的是:必须使用webhttpbehavior对服务的终结点进行配置。还要求使用webgetattribute或webinvokeattribute属性将各个服务操作映射到 uri,同时定义调用和返回结果的消息格式。
下面是我的绑定:
<webhttpbinding>
<binding name="newbinding3" maxreceivedmessagesize="2147483647" transfermode="buffered">
<readerquotas maxdepth="32" maxstringcontentlength="2147483647" maxarraylength="12000000" maxbytesperread="2147483647" maxnametablecharcount="2147483647" />
<security mode="none">
<transport clientcredentialtype="none"></transport>
</security>
</binding>
</webhttpbinding>
然后行为的配置,必须使用webhttpbehavior对服务的终结点进行配置。
如下:
<endpointbehaviors>
<behavior name="webhttp"> <!--必须设置-->
<webhttp helpenabled="true" automaticformatselectionenabled="true"/>
</behavior>
</endpointbehaviors>
因为我的wcf服务是寄宿在后台服务程序里的,会明确指定端口与ip,所以我还需要添加一个服务行为
<behavior name="restbehaviors">
<!-- 将下列元素添加到服务行为配置中。 -->
<servicemetadata httpgetenabled="true" httpgeturl="http://192.168.1.192:8008/imwcf/openapi/"/>
<servicedebug includeexceptiondetailinfaults="true" />
</behavior>
最后就是添加<services>配置终结点了:
<service name="wcfservice.openapi" behaviorconfiguration="restbehaviors">
<endpoint address="http://192.168.1.192:8008/imwcf/openapi/" binding="webhttpbinding" bindingconfiguration="newbinding3" contract="wcfcontract.iopenapi" behaviorconfiguration="webhttp">
</endpoint>
</service>
3.修改客户端调用方式
这里提供两种方式,一种是常规的通过httpwebrequest的方式,一种是直接通过webclient的方式,个人感觉第二种更方便一些。(重要的是参数数据的传递)
另外提一下,上面推荐的网址中,提供了c#编程直接访问http的方式与使用jqueryget和post访问的方式,消息的格式不一样,一种是xml,一种是json格式,这里我没做细的研究,都用的json格式。
常规方式
代码如下:
public string posthelper(string uri, string postdata)
{
try
{
encoding myencode = encoding.getencoding("utf-8");
//转换为字节数组
byte[] data = encoding.utf8.getbytes(postdata);
uri uri = new uri(uri);
httpwebrequest req = webrequest.create(uri) as httpwebrequest;
req.method = "post";
req.keepalive = true;
//req.contenttype = "application/json";
req.contenttype = "application/json;charset=utf-8";
req.contentlength = data.length; ////填充post数据
req.allowautoredirect = true;
//这个在post的时候,一定要加上,如果服务器返回错误,他还会继续再去请求,不会使用之前的错误数据,做返回数
req.servicepoint.expect100continue = false;
stream outstream = req.getrequeststream();
outstream.write(data, 0, data.length);
outstream.close();
//发送post数据请求服务器
httpwebresponse res = req.getresponse() as httpwebresponse;
//获取服务器返回信息
stream instream = res.getresponsestream();
//streamreader sr = new streamreader(instream);
streamreader sr = new streamreader(instream, myencode);
string htmlresult = sr.readtoend();
return htmlresult;
}
catch (exception ex)
{
return "网络错误:" + ex.message.tostring();
}
}
调用:
string url = "http://192.168.1.192:8008/imwcf/openapi/worksend";
string postdata = @"{'title': '"+ title + "','receiverid': '" + recevier + "','senderid': '" + sender + "','content': '" + content + "','url': '" + invokeurl + "','msgtype': '" + msgtype + "'}";
string mes = posthelper(url, postdata);
webclient方式
webclient client = new webclient();
client.headers.add("content-type", "application/json");
try
{
var result = client.uploadstring("http://192.168.1.192:8008/imwcf/openapi/postdatatest", "post", "{\"value\":\"ading\",\"content\":\"good\"}");
console.writeline("返回值为{0}", result.tostring());
}
二、问题
在配置好服务端后,客户端写好调用方式后,遇到最多并且花费我大量时间的问题就是,400错误,
远程服务器返回错误: (400) 错误的请求。
一到这里就报错,上网查找好多400错误,都没有解决,最后看到一条评论说这种问题就是,参数不匹配或者调用方式get、post搞错的导致的。
通过第三方的postman调用我的接口,发现能正常调用,排除问题出在服务端的可能,只能是自己的客户端调用或者配置存在问题,因为json转换这块不是很熟,着重排查这里,一番修改,发现参数也没什么问题。
实在没办法,在服务端添加测试接口,只传递一个参数的方法,调用后发现没问题,然后对比两个接口方法的锲约,发现缺少了bodystyle = webmessagebodystyle.wrapped,添加后,发送可以调用了,查看描述,得知bodystyle = webmessagebodystyle.wrapped设定包装参数。
添加属性bodystyle = webmessagebodystyle.wrappedrequest,
[webinvoke(bodystyle = webmessagebodystyle.wrapped,responseformat = webmessageformat.json, requestformat = webmessageformat.json, method = "post")]
并且需要保证后台传递数据中包含参数的传递
例如 string postdata = @"{""value"": ""hello""}";,value及服务中方法的参数名。
三、总结
自工作以来,一直从事cs端方向项目,对于bs端得很多知识都靠得在学校学的基础,bs端的网络协议、json数据格式等都不熟,需要好好补充一下知识了。最后感谢网上其他同仁们的无私分享。
推荐阅读