当今的软件开发中,设计软件的服务并将其通过网络对外发布,让各种客户端去使用服务已经是十分普遍的做法。就.NET而言,目前提供了Remoting,WebService和WCF服务,这都能开发出功能十分强大的服务。然而,越来越多的互联网应用,希望将服务只是通过HTTP发布出去,而不是使用复杂的SOAP协议。为了解决这个问题,ASP.NET WebAPI就出现了。
ASP.NET API简单来说就是对REST协议进行了充分的支持,可以使用HTTP中的GET,POST,PUT和DELETE进行服务的发布。在本文中,将以例子说明如何使用ASP.NET WEB API去创建一个REST风格的Web服务,并且如何编写客户端对其进行连接,此外在文中还将学会如何通过HTTP协议,使用Entity Framework和SQL Server去创建一个CRUD(增删改)的应用程序。本文的阅读对象为具备一定ASP.NET MVC基础的用户。
前期准备
为了继续本文的学习,首先需要安装ASP.NET MVC4和JSON.net,ASP.NET Web API是ASP.NET MVC 4 BETA版的一部分,可以从下面这个地址下载得到:http://www.asp.net/mvc/mvc4。
此外,还需要下载Json.NET,它是一个在.NET中高性能的对JSON进行解析处理的类库,下载地址在:http://json.codeplex.com/releases/view/82120, 在本文的学习中,将会用到Json.net。
创建ASP.NET MVC4 WEB API应用
下面,首先打开VS.NET 2010,然后建立一个新的项目,选择使用C#语言,然后选择ASP.NET MVC4 WEB APPLICATION,再选择使用Web API 模板,如下两个图所示:
在建立好工程项目后,会发现项目的目录结构跟普通的MVC结构是差不多的,但其中会发现在Controller文件夹中,会发现多一个ValuesController的类文件,这个类是Web API的控制器类,它继承了ApiController类,而不是一般MVC框架中的Controller类,代码如下:
{
...
}
ValuesController类会自动生成一个基于REST风格的脚手架,以方便用户进行编码,其中提供了GET,POST,PUT和DELETE方法。下面的表格详细列出了每个HTTP方法的含义:
要注意的是,Wep API 本身不会指定以上这些方法中每个方法的具体处理流程,比如,对于服务端的文件系统,可以使用XML数据或者自定义数据存储格式,从而在这些方法中使用,主要是根据目标的操作类型和数据的存储形式,以上这些方法可以接收所需要的数据类型。
在我们继续操作前,先在项目的根目录下创建一个新的名为API的文件夹,并且将ValuesController.cs这个文件放到其中,这样做的目的,主要是为了将Web API控制器跟普通的MVC控制器进行分离,方便今后的开发。
利用Entity Framework创建数据层
现在,往Models文件夹中,新增一个Entity Framework数据实体。我们使用的是Northwind数据库中的Customers表进行操作,命名数据实体为Northwind,如下图:
之后,选择customer表,创建一个数据实体customer如下:
Web API控制器的编码
接下来,我们对已经生成了框架的Web控制器进行完善其中的代码,代码如下:
{
//Select All
public IEnumerable Get()
{
NorthwindEntities db = new NorthwindEntities();
var data = from item in db.Customers
orderby item.CustomerID
select item;
return data.ToList();
}
//Select By Id
public Customer Get(string id)
{
NorthwindEntities db = new NorthwindEntities();
var data = from item in db.Customers
where item.CustomerID == id
select item;
return data.SingleOrDefault();
}
//Insert
public void Post(Customer obj)
{
NorthwindEntities db = new NorthwindEntities();
db.Customers.AddObject(obj);
db.SaveChanges();
}
//Update
public void Put(string id, Customer obj)
{
NorthwindEntities db = new NorthwindEntities();
var data = from item in db.Customers
where item.CustomerID == id
select item;
Customer old = data.SingleOrDefault();
old.CompanyName = obj.CompanyName;
old.ContactName = obj.ContactName;
old.Country = obj.Country;
db.SaveChanges();
}
//Delete
public void Delete(string id)
{
NorthwindEntities db = new NorthwindEntities();
var data = from item in db.Customers
where item.CustomerID == id
select item;
Customer obj = data.SingleOrDefault();
db.Customers.DeleteObject(obj);
db.SaveChanges();
}
}
其中,Get()方法返回了Customers表中的所有数据,是以LIST列表的形式返回的。customer表中的主键为CustomerId列,并且是字符串类型,因此,另外的一个get带参数的方法,是根据传入的ID参数返回了某一个customer的对象实例。
Post()方法则接收一个Customer对象作为参数,并且将其新增到数据表中去,同样,Put()方法中,接收一个customerid,然后在数据表中找出该customer对象,为该customer对象的属性重新赋值,然后再保存;最后Delete方法根据传入的CustomerID参数删除数据表中的数据并保存。
从浏览器中访问WEB API
在通过普通页面作为客户端访问Web API前,首先在浏览器中通过输入地址的方法先测试一下,如下图:
注意的是,访问API的方式为:localhost/api/customers,在实际中将要根据情况替换合适的端口,默认所有的WEB API都是通过/api根目录的方式访问的,该路由是在Global.asax下进行定义,如下:
{
...
routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/{controller}/{id}",
defaults: new { id = RouteParameter.Optional }
);
...
}
因此,现在由于我们的WEB API控制器是customers,因此如何要得到某个顾客的信息,可以如下的方式访问:
/api/customers/ALFKI
创建自定义JSON格式化器
在通过浏览器去访问WEB API时,默认的显示方式是XML。Web API框架会自动根据访问客户端的不同从而返回不同的格式的数据。现在,大多数情况下,用户希望返回的格式数据是JSON形式。然而,在本文写作时,使用默认的Web API提供的JSON格式化处理器以及Entity Framework搭配工作时,会出现一些小BUG.The entities of the EF data model have IsReference property of DataContractAttribute set to True.EF EF数据模型的实体将DataContractAttribute中的IsReference属性设置为true,如下:
[Serializable()]
[DataContractAttribute(IsReference=true)]
public partial class Customer : EntityObject
{
...
}
默认情况下,Web API使用的是DataContractJsonSerializer类进行JSON序列化。但默认的JSON序列化类不能处理这样的实体类,并且在运行期间抛出如下异常:
The type 'WebAPIDemo.Models.Customer' cannot be serialized to JSON because its IsReference setting is 'True'. The JSON format does not support references because there is no standardized format for representing references. To enable serialization, disable the IsReference setting on the type or an appropriate parent class of the type.
为了克服则个问题,可以创建一个自定义的JSON格式化器。幸运的是,有第三方的JSON序列化器给我们选择使用,比如Json.NET。在本文中,将会简单介绍使用JSON.NET去完成序列化,完整的代码可以在附件中下载。
一个自定义的序列化器主要是继承了MediaTypeFormatter的基类。我们编写的这个JSON序列化类为JsonNetFormatter,在使用前要确认你的应用工程中已经引用了Json.NET的类库,如下图:
下面我们来看下JsonNetFormatter的基础代码:
{
...
}
可以通过重写MediaTypeFormatter中的一些方法如下:
{
...
}
protected override bool CanWriteType(Type type)
{
...
}
protected override Task<object> OnReadFromStreamAsync(Type type, Stream stream,
HttpContentHeaders contentHeaders,
FormatterContext formatterContext)
{
...
}
protected override Task OnWriteToStreamAsync(Type type, object value, Stream stream,
HttpContentHeaders contentHeaders,
FormatterContext formatterContext,
TransportContext transportContext)
{
...
}
具体的代码在附件中可以详细查看。一旦创建了JSON序列化器后,就需要告诉Web API框架去替换使用原先默认的JSON序列化框架。可以在global.asx中实现:
{
HttpConfiguration config = GlobalConfiguration.Configuration;
JsonSerializerSettings settings = new JsonSerializerSettings();
settings.Converters.Add(new IsoDateTimeConverter());
JsonNetFormatter formatter = new WebAPIDemo.Http.Formatters.JsonNetFormatter(settings);
config.Formatters.Insert(0, formatter);
AreaRegistration.RegisterAllAreas();
RegisterGlobalFilters(GlobalFilters.Filters);
RegisterRoutes(RouteTable.Routes);
BundleTable.Bundles.RegisterTemplateBundles();
}
其中请注意粗体字部分,这里创建了JSON自定义器的实例,并且增加到以HttpConfiguration类的配制的序列化配制集合中。
这样,我们就可以开始在客户端中调用WEB API,并使用自定义的JSON解析器进行处理。
使用jQuery 调用WEB API
接下来,我们在Index控制器中新建立一个视图,如下图:
接下来就可以根据实际需要,决定在页面中显示customer表中的多少列,最后的页面显示如下:
当页面加载时,使用GET()方法去调出customer表的所有数据,而当使用INSERT,UPDATE,DELETE功能时,是通过jQuery去调用web api的。下面我们学习下通过jQuery去调用WEB API。
首先,我们设计每一行的HTML代码,如下:
<tr>
<th>Customer ID</th>
<th>Company Name</th>
<th>Contact Name</th>
<th>Country</th>
<th>Actions</th>
</tr>
<tr>
<td><input type="text" id="txtCustomerId" size="5"/></td>
<td><input type="text" id="txtCompanyName" /></td>
<td><input type="text" id="txtContactName" /></td>
<td><input type="text" id="txtCountry" /></td>
<td><input type="button" name="btnInsert" value="Insert" /></td>
</tr>
</table>
首先要引入jQuery类库:
然后在jQuery中,通过$.getJSON的方法,调用WEB API,代码如下:
$(document).ready(function () {
$.getJSON("api/customers", LoadCustomers);
});
熟悉jQuery的朋友肯定明白,$.getJson方法中第一个参数是调用服务的地址,第二个参数是回调方法,这个回调方法LoadCustomers中,将展示服务端web api返回的数据,代码如下:
$("#customerTable").find("tr:gt(1)").remove();
$.each(data, function (key, val) {
var tableRow = '<tr>' +
'<td>' + val.CustomerID + '</td>' +
'<td><input type="text" value="' + val.CompanyName + '"/></td>' +
'<td><input type="text" value="' + val.ContactName + '"/></td>' +
'<td><input type="text" value="' + val.Country + '"/></td>' +
'<td><input type="button" name="btnUpdate" value="Update" />
<input type="button" name="btnDelete" value="Delete" /></td>'+
'</tr>';
$('#customerTable').append(tableRow);
});
$("input[name='btnInsert']").click(OnInsert);
$("input[name='btnUpdate']").click(OnUpdate);
$("input[name='btnDelete']").click(OnDelete);
}
在上面的代码中,首先移除所有表格中的行(除了表头外),然后通过jQuery中的each方法,遍历web api返回给前端的数据,最后展现所有的数据行。然后在Insert,update,delete三个按钮中都绑定了相关的方法函数,下面先看update的代码:
var cell;
var customerId = $(this).parent().parent().children().get(0).innerHTML;
cell = $(this).parent().parent().children().get(1);
var companyName = $(cell).find('input').val();
cell = $(this).parent().parent().children().get(2);
var contactName = $(cell).find('input').val();
cell = $(this).parent().parent().children().get(3);
var country = $(cell).find('input').val();
var data = '{"id":"' + customerId + '", "obj":{"CustomerID":"' + customerId +
'","CompanyName":"' + companyName + '","ContactName":"' +
contactName + '","Country":"' + country + '"}}';
$.ajax({
type: 'PUT',
url: '/api/customers/',
data: data,
contentType: "application/json; charset=utf-8",
dataType: 'json',
success: function (results) {
$.getJSON("api/customers", LoadCustomers);
alert('Customer Updated !');
}
})
}
在上面的代码中,首先从每行的各个文本框中获得要更新的值,然后组织成JSON数据,
其数据格式为包含两项,其中一项包含customer的ID,另外一个是新的customer实体对象,因为WEB API的PUT方法需要的是两个参数。
然后通过jQuery的$.ajax方法进行调用web api,注意这里的type指定为put方法,并且注意编码为UTF-8,然后在回调方法success中,再此使用$.getJSON方法,获得更新后的最新用户列表。
而Insert,Delete的方法代码如下:
var customerId = $("#txtCustomerId").val();
var companyName = $("#txtCompanyName").val();
var contactName = $("#txtContactName").val();
var country = $("#txtCountry").val();
var data = '{"obj":{"CustomerID":"' + customerId + '","CompanyName":"' + companyName +
'","ContactName":"' + contactName + '","Country":"' + country + '"}}';
$.ajax({
type: 'POST',
url: '/api/customers/',
data: data,
contentType: "application/json; charset=utf-8",
dataType: 'json',
success: function (results) {
$("#txtCustomerId").val('');
$("#txtCompanyName").val('');
$("#txtContactName").val('');
$("#txtCountry").val('');
$.getJSON("api/customers", LoadCustomers);
alert('Customer Added !');
}
})
}
function OnDelete(evt) {
var customerId = $(this).parent().parent().children().get(0).innerHTML;
var data = '{"id":"' + customerId + '"}';
var row = $(this).parent().parent();
$.ajax({
type: 'DELETE',
url: '/api/customers/',
data: data,
contentType: "application/json; charset=utf-8",
dataType: 'json',
success: function (results) {
$.getJSON("api/customers", LoadCustomers);
alert('Customer Deleted!');
}
})
}
读者要注意的是,在实际应用中,可以使用含有GET,PUT,DELETE前缀的方法名,比如
GetXXXX(), PutXXXX(), PostXXXX()都是可以的,XXX是自定义的名称,WEB API框架依然会调用对应的GET,PUT和POST方法。
最后运行后,效果如下图:
WebForm形式调用WEB API
尽管ASP.NET WEB API是ASP.NET MVC的其中一部分,但并没规定只能在ASP.NET MVC架构中使用,可以在WebForm中进行调用,方法如下:
我们继续在解决方案中新建一个Web Application,然后在应用中增加一个普通的asp.net Web Form页面,然后将之前的API文件夹复制到这个新的web项目的根目录中。
然后和之前的步骤一样,通过Entitiy Framework建立customer实体类,然后打开Global.ascx,写入代码如下:
{
HttpConfiguration config = GlobalConfiguration.Configuration;
JsonSerializerSettings settings = new JsonSerializerSettings();
settings.Converters.Add(new IsoDateTimeConverter());
JsonNetFormatter formatter = new WebAPIWebFormDemo.Http.Formatters.JsonNetFormatter(settings);
config.Formatters.Insert(0, formatter);
RouteTable.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/{controller}/{id}",
defaults: new { id = System.Web.Http.RouteParameter.Optional }
);
}
注意这里做了两个事情,首先在WEB API框架中注册了自定义的JSON解析器,然后是注册了web api 控制器的路由。
然后将前文说到的使用jQuery 调用web api的所有代码都复制到index.aspx(或default.aspx)中去。然后运行应用,如下图:
可以看到浏览器的地址栏,这里是使用传统的web form表单进行访问的。
小结
本文介绍了ASP.NET MVC 4中新推出的符合REST架构的WEB API,并以实际例子讲解了如何通过不同的方式调用web api返回的结果。本文的附件是代码,请在这个地址下载: