项目需要要和别人对接,对方用的是RestFul接口,Java平台的,我们是.NET平台的,需要编写RestFul调用客户端,为了测试也要相应的服务端,虽然我也会Java语言,但是不熟啊,搞个环境都要一两天。还是用c#模拟一下就好了。
看了点《RESTful Web APIs中文版》电子版的,还在看理论阶段,电子版的只有一半没找到完整版的。
一、RestFul服务端
参考:https://blog.csdn.net/boonya/article/details/46424099
按我理解是用WCF的服务来实现的RestFul,在OperationContract的基础上增加WebGet特性,使之能够路由到相应的函数进行处理。
WCF是微软通信相关技术的集成,RestFul是一种架构风格,和一般的WCF服务的区别就是{id}这种参数的路由了吧。
我是要在前面参考的基础上再扩展一下的。
1.修改Uri或者变量
原来的是
[ServiceContract(Name = "RestDemoServices")]
public interface IRestDemoServices
{
[OperationContract]
[WebGet(UriTemplate = "/Client/{id}", BodyStyle=WebMessageBodyStyle.Bare)]
string GetClientNameById(string id);
}
1.1修改变量名id为name,程序可以运行,但是出现异常,结论:方法变量名和Url里面的变量名必须相同。
An unhandled exception of type 'System.InvalidOperationException' occurred in System.ServiceModel.Web.dll
Additional information: 约定“RestDemoServices”中的操作“GetClientNameById”应具有名称为“ID”的参数的 UriTemplate,但该操作并没有此名称的输入参数。
1.2 修改变量名为ID,没问题,结论:大小写无关。
1.3 将id的类型改为int,出现异常,
Additional information: 约定“RestDemoServices”中的操作“GetClientNameById”具有非“字符串”类型的路径变量“id”。UriTemplate 路径段的变量类型必须为“字符串”。
搜索发现,路径变量必须是string类型,参数可以是int类型
[OperationContract]
[WebGet(UriTemplate = "/Clients/{id}", BodyStyle = WebMessageBodyStyle.Bare, ResponseFormat = WebMessageFormat.Json)]
Client GetClientById(string id);
[OperationContract]
[WebGet(UriTemplate = "/Clients/?id={id}", BodyStyle = WebMessageBodyStyle.Bare, ResponseFormat = WebMessageFormat.Json)]
Client QueryClientById(int id);
public Client GetClientById(string id)
{
string result = string.Format("[{0}][GetClientById] id is : {1}", DateTime.Now, id);
Client client = Clients.Find(i => i.Id.ToString() == id);
if (client == null) return null;
client.Message = result;
return client;
}
public Client QueryClientById(int id)
{
string result = string.Format("[{0}][QueryClientById] id is : {1}", DateTime.Now, id);
Client client = Clients.Find(i => i.Id == id);
if (client == null) return null;
client.Message = result;
return client;
}
不过这样的话会出现使用http://localhost:8111/DemoService/Clients/,会跳到QueryClientById,并且id=0的情况。
1.4 不同方法,相同的Uri,出现异常:
[ServiceContract(Name = "RestDemoServices")]
public interface IRestDemoServices
{
[OperationContract]
[WebGet(UriTemplate = "/Client/{id}", BodyStyle=WebMessageBodyStyle.Bare)]
string GetClientNameById(string id);
[OperationContract]
[WebGet(UriTemplate = "/Client/{id}", BodyStyle = WebMessageBodyStyle.Bare)]
string GetClientById(string id);
}
Additional information: 在约定“RestDemoServices”中,有多个方法为“GET”且 UriTemplate 为“/Client/{id}”的操作。每个操作都需要 UriTemplate 和方法的唯一组合才能明确地调度消息。请使用 WebGetAttribute 或 WebInvokeAttribute 改变操作的 UriTemplate 和方法值。
1.5 uri变量不在最后,没问题。
[ServiceContract(Name = "RestDemoServices")]
public interface IRestDemoServices
{
[OperationContract]
[WebGet(UriTemplate = "/Client/{id}/name", BodyStyle=WebMessageBodyStyle.Bare)]
string GetClientNameById(string id);
[OperationContract]
[WebGet(UriTemplate = "/Client/{id}", BodyStyle = WebMessageBodyStyle.Bare)]
string GetClientById(string id);
}
1.6 去掉OperationContract,没问题
[ServiceContract(Name = "RestDemoServices")]
public interface IRestDemoServices
{
//[OperationContract]
string Test();
//[OperationContract]
[WebGet(UriTemplate = "/Clients", BodyStyle = WebMessageBodyStyle.Bare, ResponseFormat = WebMessageFormat.Json)]
Client[] GetClients();
//[OperationContract]
[WebGet(UriTemplate = "/Clients/{id}", BodyStyle = WebMessageBodyStyle.Bare, ResponseFormat = WebMessageFormat.Json, RequestFormat = WebMessageFormat.Json)]
Client GetClientById(string id);
//[OperationContract]
[WebGet(UriTemplate = "/Clients/{id}/name", BodyStyle=WebMessageBodyStyle.Bare,ResponseFormat = WebMessageFormat.Json)]
Client GetClientNameById(string id);
}
这样考虑的话,有OperationContract是不是即是RestFul风格的也是RPC风格的,后面客户端再测试一下。
1.7 参数
[WebGet(UriTemplate = "/Clients?q1={q1}&q2={q2}", BodyStyle = WebMessageBodyStyle.Bare, ResponseFormat = WebMessageFormat.Json)]
Client[] GetClients(string q1,int q2);
可以只设置某一个,也可以都不设置;没有设置参数的情况下,string类型为null,int类型为0。
1.8 参数和路径变量 下面的设置可以生成,运行会出异常
//[OperationContract]
[WebGet(UriTemplate = "/Clients?q1={q1}&q2={q2}", BodyStyle = WebMessageBodyStyle.Bare, ResponseFormat = WebMessageFormat.Json)]
Client[] GetClients(int q1,int q2);
[WebGet(UriTemplate = "/Clients/{q1}/{q2}", BodyStyle = WebMessageBodyStyle.Bare, ResponseFormat = WebMessageFormat.Json)]
Client[] GetClients(string q1, string q2);
Additional information: 同一个协定中不能存在两个名称相同的操作,类型为 RestFulServiceTest.IRestDemoServices 的方法 GetClients 和 GetClients 违反了此规则。可以通过更改方法名称或使用 OperationContractAttribute 的 Name 属性更改其中一个操作的名称。
改成下面就可以了:
[WebGet(UriTemplate = "/Clients?q1={q1}&q2={q2}", BodyStyle = WebMessageBodyStyle.Bare, ResponseFormat = WebMessageFormat.Json)]
Client[] QueryClients(int q1,int q2);
[WebGet(UriTemplate = "/Clients/{q1}/{q2}", BodyStyle = WebMessageBodyStyle.Bare, ResponseFormat = WebMessageFormat.Json)]
Client[] GetClients(string q1, string q2);
http://localhost:8111/DemoService/Clients/1 进入GetClientById
http://localhost:8111/DemoService/Clients/1/2 进入 GetClients
1.9 复杂路径变量
[WebGet(UriTemplate = "/Clients/{q1},{q2},{q3}", BodyStyle = WebMessageBodyStyle.Bare, ResponseFormat = WebMessageFormat.Json)]
Client[] QueryClientsEx(string q1, string q2, string q3);
这样也是可以的,但是这里的string改成int就不行了,这些q1,q2,q3是路径变量。
类似的
[WebGet(UriTemplate = "/Clients/{q1},{q2},{q3}/{id}", BodyStyle = WebMessageBodyStyle.Bare, ResponseFormat = WebMessageFormat.Json)]
Client GetClientByIdEx(string q1, string q2, string q3,string id);
[WebGet(UriTemplate = "/Clients/{a}_{b}/{id}", BodyStyle = WebMessageBodyStyle.Bare, ResponseFormat = WebMessageFormat.Json)]
Client GetClientByIdEx2(string a, string b, string id);
也可以
2.返回json格式
WebGet中设置ResponseFormat = WebMessageFormat.Json,默认的其实是Xml格式,返回结果为类时就能看到结构了
3.post,put,delete
[WebInvoke(UriTemplate = "/Clients",Method = "POST", ResponseFormat = WebMessageFormat.Json, RequestFormat = WebMessageFormat.Json)]
Client CreateClient(Client client);
[WebInvoke(UriTemplate = "/Clients", Method = "PUT", ResponseFormat = WebMessageFormat.Json, RequestFormat = WebMessageFormat.Json)]
Client UpdateClient(Client client);
[WebInvoke(UriTemplate = "/Clients/{id}", Method = "DELETE", ResponseFormat = WebMessageFormat.Json, RequestFormat = WebMessageFormat.Json)]
Client DeleteClient(string id);
public Client CreateClient(Client client)
{
IdFlag++;
client.Id = IdFlag;
client.Message = "CREATE";
Clients.Add(client);
return client;
}
public Client UpdateClient(Client clientNew)
{
Client client = Clients.Find(i => i.Id == clientNew.Id);
client.Name = clientNew.Name;
client.Message = "UPDATE";
return client;
}
public Client DeleteClient(string id)
{
Client client = Clients.Find(i => i.Id+"" == id+"");
if (client == null) return null;
Clients.Remove(client);
client.Message = "DELETE";
return client;
}
注意,在写delete时UriTemplate中少了{id}服务端也是能正常运行的,但是客户端出现403错误。
而且路径参数中的路径变量和方法中的变量不需要一一对应,可能是WebInvoke的检查没有WebGet严格吧,自己要注意。
像{id}少了的情况是不行的,但是方法中多出路径中没有的变量则没有影响,只是参数没用
[WebInvoke(UriTemplate = "/Clients/{id}", Method = "DELETE", ResponseFormat = WebMessageFormat.Json, RequestFormat = WebMessageFormat.Json)]
Client DeleteClient(string id,string name);
4.deleteAll
[WebInvoke(UriTemplate = "/Clients", Method = "DELETE", ResponseFormat = WebMessageFormat.Json, RequestFormat = WebMessageFormat.Json)]
string DeleteAllClients();
5.post,put数组
[WebInvoke(UriTemplate = "/Client",Method = "POST", ResponseFormat = WebMessageFormat.Json, RequestFormat = WebMessageFormat.Json)]
Client CreateClient(Client client);
[WebInvoke(UriTemplate = "/Clients", Method = "POST", ResponseFormat = WebMessageFormat.Json, RequestFormat = WebMessageFormat.Json)]
Client[] CreateClients(Client[] client);
[WebInvoke(UriTemplate = "/Client", Method = "PUT", ResponseFormat = WebMessageFormat.Json, RequestFormat = WebMessageFormat.Json)]
Client UpdateClient(Client client);
[WebInvoke(UriTemplate = "/Clients", Method = "PUT", ResponseFormat = WebMessageFormat.Json, RequestFormat = WebMessageFormat.Json)]
Client[] UpdateClients(Client[] client);
因为相同类型Method不能有多个相同的UriTemplate,所有将对当个的处理改成client,对多个的处理改成clients。
客户端测试代码放到下一篇客户端部分中。