首先,给wcf restful下个定义,WCF 很好的支持了 REST 的开发, 而 RESTful 的服务通常是架构层面上的考虑。 因为它天生就具有很好的跨平台跨语言的集成能力,几乎所有的语言和网络平台都支持 HTTP 请求,无需去实现复杂的客户端代理,无需使用复杂的数据通讯方式既可以将我们的服务暴露给任何需要的人,无论他使用 VB、Ruby、JavaScript,甚至是 HTML FORM,或者直接在浏览器地址栏输入。
WCF 中通过 WebGetAttribute、WebInvokeAttribute (GET/PUT/POST/DELETE)、UriTemplate 定义 REST 的服务的调用方式, 通过 WebMessageFormat (Xml/Json) 定义消息传递的格式。
在网上有很多关于如何使用WCF Restful的教程,我也学习了不少,但是网上的教程发布是通过WebServiceHost的,而我想实现的是通过svc文件发布的方式来实现。在这种方式中,配置文件上我碰到了很多问题,但最终我实现了这个方式。最终发现很简单,可能是我对WCF不够了解。例子很简单,我主要对这两种方式的不同做个比较。
通过WebServiceHost发布
1. 定义接口,与soap格式不同,在方法上不需要OperationContract,而是WebGet或WebInvoke。WebGet表示该方法是通过Http Get请求的。WebInvoke可以定义Method,这里是Post请求。
namespace WcfServiceRestTest
{
[ServiceContract]
public interface IUserService
{
[WebGet(UriTemplate="{id}")]
UserInfo GetUser(string id);
[WebGet(UriTemplate = "all",ResponseFormat=WebMessageFormat.Json)]
IEnumerable<UserInfo> GetAll();
[WebInvoke(UriTemplate = "Create",Method="Post", ResponseFormat = WebMessageFormat.Json)]
int Create(UserInfo info);
}
}
1.接口实现,这里有个小细节,对于参数id,本来是int值,但是wcf运行后不支持,报错错误“约定“IUserService”中的操作“GetUser”具有非“字符串”类型的路径变量“id”。UriTemplate 路径段的变量类型必须为“字符串”。”,所以改成类型string,说明wcf只支持string类型的参数。
namespace WcfServiceRestTest
{
public class UserService : IUserService
{
List<UserInfo> userList;
public UserService()
{
userList = new List<UserInfo>
{
new UserInfo{ID= 1, Name= "Wesley"},
new UserInfo{ID= 2, Name= "Jack"},
new UserInfo{ID= 2, Name= "Eric"}
};
}
public UserInfo GetUser(string id)
{
return userList.Where(o => o.ID == int.Parse(id)).FirstOrDefault();
}
public IEnumerable<UserInfo> GetAll()
{
return userList;
}
public int Create(UserInfo info)
{
if (userList != null && info != null)
{
userList.Add(info);
return 1;
}
else
{
return 0;
}
}
}
}
3.服务配置,Address Binding Contract,与WCF SOAP类似,不过需要注意如下红色部分,webHttpBinding,这是Restful格式的绑定方式。endpoint的address如监听地址一样,如果请求到这儿“http://127.0.0.1:11191/UserService”,就会调用此服务。
<?xml version="1.0"?>
<configuration>
<system.serviceModel>
<services>
<service name="WcfServiceRestTest.UserService">
<endpoint address="http://127.0.0.1:11191/UserService" binding="<span style="color:#FF0000;">webHttpBinding</span>" contract="WcfServiceRestTest.IUserService"/>
</service>
</services>
</system.serviceModel>
<system.web>
<compilation debug="true" targetFramework="4.0"/>
<pages controlRenderingCompatibilityVersion="3.5" clientIDMode="AutoID"/>
</system.web>
</configuration>
4.启动服务
class Program
{
static void Main(string[] args)
{
using (WebServiceHost host = new WebServiceHost(typeof(UserService)))
{
host.Open();
Console.Read();
}
}
}
5.启动程序后,便可以通过endpoint的address加上方法的UriTemplate去请求。如 http://127.0.0.1:11191/UserService/all,调用服务中GetAll方法。
通过svc文件发布
代码跟上面的一样,只是配置文件不一样,如下所示:
<?xml version="1.0" encoding="UTF-8"?>
<configuration>
<system.web>
<compilation debug="true" targetFramework="4.0" />
</system.web>
<system.serviceModel>
<services>
<service name="WcfServiceRestTest.UserService" behaviorConfiguration="ServiceBehavior">
<endpoint binding="webHttpBinding" contract="WcfServiceRestTest.IUserService" behaviorConfiguration="webHttp" />
</service>
</services>
<behaviors>
<serviceBehaviors>
<behavior name="ServiceBehavior">
<serviceMetadata httpGetEnabled="true" />
<serviceDebug includeExceptionDetailInFaults="false" />
</behavior>
<behavior>
<!-- To avoid disclosing metadata information, set the value below to false and remove the metadata endpoint above before deployment -->
<serviceMetadata httpGetEnabled="true" />
<!-- To receive exception details in faults for debugging purposes, set the value below to true. Set to false before deployment to avoid disclosing exception information -->
<serviceDebug includeExceptionDetailInFaults="false" />
</behavior>
</serviceBehaviors>
<endpointBehaviors>
<behavior name="webHttp">
<webHttp />
</behavior>
</endpointBehaviors>
</behaviors>
</system.serviceModel>
<system.webServer>
<modules runAllManagedModulesForAllRequests="true" />
<directoryBrowse enabled="true" />
</system.webServer>
</configuration>
这种方式让我测试了很久。其实这里只是没有定义address,别的都差不多。
这样就可以通过 localhost + web项目定义的端口号 + svc文件名称 + UriTemplate 来访问了。如 http://localhost:11191/UserService.svc/all,这请求中与上面的请求略有不同。
这个请求有.svc,是先找到svc,再去调用svc对应的服务。
之前我在配置中一直有address,总是报“没有与给定的地址“http://127.0.0.1:11191/UserService”匹配的协议绑定。协议绑定在 IIS 或 WAS 配置中的站点级别配置。”的错误。
通过svc文件发布不需要给servcie定义address,我想了下原因,可能是因为wcf服务项目已经定义了ip和端口号,所以不需要再定义address了。
写此篇在这里做个记录,让自己对WCF加深印象。如果有不对的地方,希望各位不吝赐教。