在我前一篇博客中已经给各位简单介绍了http协议与restful api的关系,以及一些基本的http协议知识,在这些知识的铺垫下,今天,我们一起来讨论一下web api的适用场景,然后写我们第一个web api接口,并演示如何对其进行简单调用。
很多人都很迷惑,既然有了wcf为什么还要有web api?web api会不会取代wcf?
就我的看法,wcf提供的是一种rpc实现的集合,wcf的设计更多地考虑了soa的场景,以及各种rpc的问题。很多人也会说,restful api也是一种rpc啊,并且wcf中也有关于restful 的实现啊。很多资料中rpc和restful在风格概念上是有一些区别的,其实我觉得这两者的区别比较主观,过度纠结这些就学院派了;我主要关注了实际使用上的一些问题,在wcf中,支持的协议很多,ws-*系列协议,以及一些更简洁的协议,其中提供了一些专用通信协议的性能是非常高的,并且wcf还提供了服务发现等功能,我认为wcf更适合内部系统间的高性能调用,社区中也有其他一些rpc方案可以选择,例如grpc,avor,thrift都是和wcf定位相同的产品;而web api是关注于http restful风格的产品,在此基础上,任何语言、任何终端都能非常容易地进行对接,并且能利用非常成熟的各种http基础设施和解决方案来进行开发、调试、负载均衡、内容分发。所以,web api是一种针对http的,偏重于快速开发restful风格开放式api的开发框架。目前看来,他并不能取代wcf,他们各有适合的场景,不能认为web api是wcf的替代产品。
ok,现在我们来开发第一组web api接口!使用vs2012以后的版本都有现成的web api创建模板,大家跟着创建就好了,创建出来后,项目中会有mvc、web api的项目,web api对mvc有依赖,不能单独创建!而web api和mvc都是利用类似的路由机制,所以在默认路由中,web api 使用
/api/{controller}/{id}
作为路由,添加了/api/节以区分mvc和web api。
接下来,我们添加一个web api的controller,取名为personcontroller,他继承于apicontroller;在创建这个controller的时候,我们就定义了一种资源:person,在personcontroller里的所有操作均围绕着person这个资源来的。接下来我们开始定义一组增删改查操作。
在web api中,默认路由采用了一种约定:根据谓词来进行路由,而方法名的前缀就是调用该方法对应使用的http谓词。代码示例如下:
/// <summary>
/// person 为资源,对person进行的一组操作 /// </summary>
public class personcontroller : apicontroller
{ private static list<person> _personlst = new list<person>();
/// <summary>
/// 获取一个person /// </summary>
/// <param name="id">person的id</param>
/// <returns>person</returns>
public person getperson(long id)
{ return _personlst.find(x => x.id == id);
}
/// <summary>
/// 添加一个person /// </summary>
/// <param name="person">person</param>
public void postaddperson(person person)
{
_personlst.add(person);
}
/// <summary>
/// 修改一个 /// </summary>
/// <param name="id">person id</param>
/// <param name="person">新</param>
public void putmodifyperson(long id, person person)
{ var p = _personlst.find(x => x.id == id);
p.age = person.age;
p.name = person.name;
p.sex = person.sex;
}
/// <summary>
/// 删除一个person /// </summary>
/// <param name="id">person id</param>
public void deleteperson(long id)
{
_personlst.removeall(x => x.id == id);
}
}
一个简单的针对资源的crud操作的api就好了,不用解析输入,不用拼接输出,就是那么简单!让我们来遛一遛!
发送请求:谓词为post,语义创建person,person描述在body里,head中声明了body通过json序列化。
收到响应:响应码204,属于2xx类型执行成功,body里没有数据
发送请求:谓词为get,语义为查询person资源,id为1的,head中声明希望接收使用xml序列化的数据
收到响应:响应码为200,执行成功,body中有数据,数据使用xml序列化
发送请求:谓词为put,语义为修改id为1的person资源,修改内容在body中,content-type标明body使用json序列化,在body中我们将name修改为test1changed
收到响应,响应码为204,执行成功
发送请求:谓词为get,语义为查询id为1的person资源,accept标明希望接收到json数据
收到响应:可以看到body为使用json序列化的内容,name属性已经变更为test1changed
发送请求:谓词为delete,语义为删除id为1的person资源
收到响应:响应码204,执行成功
发送请求:谓词为get,语义为查询id为1的person资源,accept标明希望接收到json数据
收到响应:响应码为200,执行成功,响应内容为null,资源已删除
这就是我用fiddler来发送、调用的一组restful接口,大家可以看到,整个调用过程使用到了http的语义,用到了谓词路由、内容协商。在增、删、改操作中,我都是使用void作为返回值,根据http code 判断,大家也可以自定义一些返回数据来做出更进一步的操作描述。
在写了这些api后,我们需要在程序中调用,我以c#为例写一组对这些接口调用的实现。在c#中,传统调用http接口一般有两种办法: webrequest/webresponse组合的方法调用和webclient类进行调用。第一种方法抽象程度较低,使用较为繁琐;而webclient主要面向了web网页场景,在模拟web操作时使用较为方便,但用在restful场景下却比较麻烦,在web api发布的同时,.net提供了两个程序集:system.net.http和system.net.http.formatting。这两个程序集中最核心的类是httpclient。在.net4.5中带有这两个程序集,而.net4需要到nuget里下载microsoft.net.http和microsoft.aspnet.webapi.client这两个包才能使用这个类,更低的.net版本就只能表示遗憾了只能用webrequest/webresponse或者webclient来调用这些api了。
在使用中,system.net.http这个程序集提供了httpclient类以及相关的http调用,而system.net.http.formatting提供了一些针对httpclient的帮助扩展,更好地支持了内容协商、content创建等功能。下面我就和大家一起写一下这个例子:
我们新建一个控制台程序:
代码如下:
public class person
{ public long id { get; set; } public string name { get; set; }
public int age { get; set; }
public string sex { get; set; }
public override string tostring()
{ return $id={id} name={name} age={age} sex={sex};
}
}
class program
{ static void main(string[] args)
{ var client = new httpclient();
client.baseaddress = new uri(http://localhost:22658/); //基本的api url
client.defaultrequestheaders.accept.add(new mediatypewithqualityheadervalue(application/json)); //默认希望响应使用json序列化
run(client);
console.readline();
}
static async void run(httpclient client)
{ var result = await addperson(client);
console.writeline($添加结果:{result}); //添加结果:true
var person = await getperson(client);
console.writeline($查询结果:{person}); //查询结果:id=1 name=test age=10 sex=f
result = await putperson(client);
console.writeline($更新结果:{result}); //更新结果:true
result = await deleteperson(client);
console.writeline($删除结果:{result}); //删除结果:true
}
static async task<bool> addperson(httpclient client)
{ return await client.postasjsonasync(api/person, new person() { age = 10, id = 1, name = test, sex = f }) //向person发送post请求,body使用json进行序列化
.continuewith(x => x.result.issuccessstatuscode); //返回请求是否执行成功,即http code是否为2xx
}
static async task<person> getperson(httpclient client)
{ return await await client.getasync(api/person/1) //向person发送get请求
.continuewith(x => x.result.content.readasasync<person>( //获取返回body,并根据返回的content-type自动匹配格式化器反序列化body
new list<mediatypeformatter>() {new jsonmediatypeformatter()/*这是json的格式化器*/
,new xmlmediatypeformatter()/*这是xml的格式化器*/}));
}
static async task<bool> putperson(httpclient client)
{ return await client.putasjsonasync(api/person/1, new person() { age = 10, id = 1, name = test1change, sex = f }) //向person发送put请求,body使用json进行序列化
.continuewith(x => x.result.issuccessstatuscode); //返回请求是否执行成功,即http code是否为2xx
}
static async task<bool> deleteperson(httpclient client)
{ return await client.deleteasync(api/person/1) //向person发送delete请求
.continuewith(x => x.result.issuccessstatuscode); //返回请求是否执行成功,即http code是否为2xx
}
}
这就完成了这组api的调用,是不是非常简单方便?httpclient使用全异步的方法,并且他有良好的扩展性,我会在之后的博客中再聊这个问题。
ok,到此为止一组简单的restful api和c#的调用客户端就完成了,但这只是开始,web api是一个很强大的框架,他的扩展点非常丰富,这些扩展能为我们的开发提供很多的帮助,下一篇博文我将为大家带来web api中filter的使用。
博文中如有不正确的地方欢迎大家指正。
以上就是asp.net web api的适用场景第一个实例 的详细内容。