ASP.NET WebAPI从入门

在新出的MVC4中,增加了WebAPI,用于提供REST风格的WebService,新生成的WebAPI项目和典型的MVC项目一样,包含主要的Models、Views、Controllers等文件夹和Global.asax文件。Views对于WebAPI来说没有太大的用途,Models中的Model主要用于保存Service和Client交互的对象,这些对象默认情况下会被转换为Json格式的数据迚行传输,Controllers中的Controller对应于WebService来说是一个Resource,用于提供服务。和普通的MVC一样,Global.asax用于配置路由规则。  环境准备       建议使用VS2012以上版本创建WebAPI,如果是使用VS2010,需要安装VS2010 SP1升级包,MVC4升级包,打开VS2012创建如下:  第一步:新建ASP.NET Web应用程序

ASP.NET WebAPI从入门

第二步:建议WebAPI

ASP.NET WebAPI从入门

新生成的WebAPI项目和典型的MVC项目一样,包含主要的Models,Views,Controllers等文件夹和Global.asax文件

ASP.NET WebAPI从入门

注意:再次强调Views对于WebAPI来说没有太大的用途,Models中的Model主要用于保存Service和Client交互的对象,这些对象默认情况下会被转换为Json格式的数据进行传输,Controllers中的Controller对应于WebService来说是一个Resource,用于提供服务。和普通的MVC一样,Global.asax用于配置路由规则

(二)Models  WCF中的数据契约形成鲜明对比的是,MVC WebAPI中的Model就是简单的POCO,没有任何别的东西,如,你可以创建如下的Model 

public class UserModel 
{ 
        public int Id { get; set; } 
        public string UserName { get; set; } 
        public string PassWord { get; set; } 
} 

注意:Model必须提供public的属性,用于json或xml反序列化时的赋值  (三)Controllers  MVC WebAPI中的Controllers和普通MVC的Controllers类似,不过不再继承于Controller,而改为继承API的ApiController,一个Controller可以包含多个Action,这些Action响应请求的方法与Global中配置的路由规则有关,在后面结束Global时统一说明    (四)Global  默认情况下,模板自带了两个路由规则,分别对应于WebAPI和普通MVC的Web请求,默认的WebAPI路由规则如下 

            routes.MapHttpRoute( 
                 name: "DefaultApi", 
                 routeTemplate: "api/{controller}/{id}", 
                 defaults: new { id = RouteParameter.Optional } 
             ); 

可以看到,默认路由使用的固定的api作为Uri的先导,按照微软官方的说法,用于区分普通Web请求和WebService的请求路径:  可以看到,默认的路由规则只指向了Controller,没有指向具体的Action,因为默认情况下,对于Controller中的Action的匹配是和Action的方法名相关联的:具体来说,如果使用上面的路由规则,对应下面的Controller:

ASP.NET WebAPI从入门
public class UserController : ApiController 
    { 
        public  List<UserModel> allModeList = new List<UserModel>() {  
          new UserModel(){ Id=1,UserName="zhang", PassWord="123"}, 
          new UserModel(){ Id=2,UserName="lishi", PassWord="123456"}, 
          new UserModel(){ Id=3,UserName="wang", PassWord="1234567"} 
        }; 
        //Get  api/User/ 
        public IEnumerable<UserModel> GetAll() 
        { 
            return allModeList; 
        } 
        //Get api/User/1 
        public IEnumerable<UserModel> GetOne(int id) { 
            return allModeList.FindAll((m) => { return m.Id == id; }); 
        } 
        //POST api/User/ 
        public bool PostNew(UserModel user) 
        { 
            try 
            { 
                allModeList.Add(user); 
                return true; 
            } 
            catch { 
                return false; 
            } 
        } 
        //Delete api/User/ 
        public int DeleteAll() 
        { 
            return allModeList.RemoveAll((mode) => { return true; }); 
        } 
        //Delete api/User/1 
        public int DeleteOne(int id) { 
            return allModeList.RemoveAll((m) => { return m.Id == id; }); 
        } 
        //Put api/User  
        public int PutOne(int id, UserModel user) 
        { 
            List<UserModel> upDataList = allModeList.FindAll((mode) => { return mode.Id == id; }); 
            foreach (var mode in upDataList) 
            { 
                mode.PassWord = user.PassWord; 
                mode.UserName = user.UserName; 
            } 
            return upDataList.Count; 
        } 
} 
ASP.NET WebAPI从入门

则,会有下面的对应关系:

URL HttpMethod 对应的Action
/api/User GET GetALL
/api/User/1 GET GetOne
/api/User POST PostNew
/api/User/1 DELETE DeleteOne
/api/User DELETE DeleteALL
/api/User PUT PutOne

  

客户端JS调用

ASP.NET WebAPI从入门
function getAll() { 
        $.ajax({ 
            url: "api/User/", 
            type: 'GET', 
            success: function (data) { 
                document.getElementById("modes").innerHTML = ""; 
                $.each(data, function (key, val) { 
                    var str = val.UserName + ': ' + val.PassWord; 
                    $('<li/>', { html: str }).appendTo($('#modes')); 
                }); 
            } 
        }).fail( 
        function (xhr, textStatus, err) { 
            alert('Error: ' + err); 
        }); 
   } 
function find() { 
        $.ajax({ 
            url: "api/User/1" , 
            type: 'GET', 
            success: function (data) { 
                document.getElementById("modes").innerHTML = ""; 
                $.each(data, function (key, val) { 
                    var str = val.UserName + ': ' + val.PassWord; 
                    $('<li/>', { html: str }).appendTo($('#modes')); 
                }); 
            } 
        }).fail( 
        function (xhr, textStatus, err) { 
            alert('Error: ' + err); 
        }); 
    } 
 
 
    function add() { 
 
        $.ajax({ 
            url: "api/User/", 
            type: "POST", 
            dataType: "json", 
            data: { "Id":4,"UserName": "admin", "PassWord": "666666"}, 
            success: function (data) { 
                getAll(); 
            } 
        }).fail( 
        function (xhr, textStatus, err) { 
            alert('Error: ' + err); 
        }); 
 
    } 
 
    function removeUser() { 
        $.ajax({ 
            url: "api/User/3", 
            type: 'DELETE', 
            success: function (data) { 
                document.getElementById("modes").innerHTML = ""; 
                getAll(); 
            } 
        }).fail( 
        function (xhr, textStatus, err) { 
            alert('Error: ' + err); 
        }); 
    } 
 
    function removeAll() { 
        $.ajax({ 
            url: "api/User/", 
            type: 'DELETE', 
            success: function (data) { 
                document.getElementById("modes").innerHTML = ""; 
                getAll(); 
            } 
        }).fail( 
        function (xhr, textStatus, err) { 
            alert('Error: ' + err); 
        }); 
    } 
    function udpate() { 
        $.ajax({ 
            url: "api/User/1", 
            type: 'PUT', 
            dataType: "json", 
            data: { Id: 1, "UserName": "admin", "PassWord": "666666" }, 
            success: function (data) { 
                document.getElementById("modes").innerHTML = ""; 
                getAll(); 
            } 
        }).fail( 
        function (xhr, textStatus, err) { 
            alert('Error: ' + err); 
        }); 
    } 
ASP.NET WebAPI从入门

这样就实现了最基本的CRUD操作。  扩展需求  问题1:我想按照用户名称(UserName)进行查询,怎么办?  办法:第一步:在UserController类中加一个方法名称叫:GetUserByName,如下所示: 

public UserModel GetUserByName(string userName) { 
            return allModeList.Find((m) => { return m.UserName.Equals(userName); }); 
} 

第二步:在客户端index.cshtml中调用 

ASP.NET WebAPI从入门
function getUserByName() { 
        $.ajax({ 
            url: "api/User/zhang", 
            type: 'GET', 
            success: function (data) { 
                document.getElementById("modes").innerHTML = ""; 
                var str = data.UserName + ': ' + data.PassWord; 
                $('<li/>', { html: str }).appendTo($('#modes')); 
            } 
        }).fail( 
        function (xhr, textStatus, err) { 
            alert('Error: ' + err); 
        }); 
   } 
ASP.NET WebAPI从入门

如果URL是: url: "api/User/zhang",将会报错:Bad Request  原因是他会自动调用我们的GetOne(int id) 这个方法,类型转换出错  解决办法:  改变URL为: url: "api/User/?userName=zhang",  问题2:我想按用户名称(UserName) 和用户密码(PassWord)一起来进行查询,怎么办?  解决办法  第一步:UserController类中,可以重载一个GetUserByName的方法,如下所示: 

public UserModel GetUserByName(string userName) { 
            return allModeList.Find((m) => { return m.UserName.Equals(userName); }); 
        } 

第二步:客户端调用:

ASP.NET WebAPI从入门
function getUserByName() { 
        $.ajax({ 
            url: "api/User/?userName=zhang&passWord=123", //这里尤其需要注意 
            type: 'GET', 
            success: function (data) { 
                document.getElementById("modes").innerHTML = ""; 
                var str = data.UserName + ': ' + data.PassWord; 
                $('<li/>', { html: str }).appendTo($('#modes')); 
            } 
        }).fail( 
        function (xhr, textStatus, err) { 
            alert('Error: ' + err); 
        }); 
   } 
ASP.NET WebAPI从入门

路由规则扩展  和普通的MVC一样,MVC WebAPI支持自定义的路由规则,如:在上面的操作中,路由规则使用  "api/{controller}/{id}"  则限定了使用GET方式利用URL来传值时,controller后面的接收参数名为id,但是在Controller中,如果GetOne方法的接收参数名为key,是不会被匹配的,这是只需要新增一个新的路由规则,或修改原先的路由规则为:  "api/{controller}/{key}",如下所示:

config.Routes.MapHttpRoute( 
                name: "DefaultApi", 
                routeTemplate: "api/{controller}/{key}", 
                defaults: new { key = RouteParameter.Optional } 
            ); 

当然,可以对路由进行更深的扩展,如:扩展成和普通MVC一样的路由:  "api/{controller}/{action}/{id}"  这样,就要求同时使用Action和HTTP方法进行匹配当然,根据微软的说法,这种使用是不被推荐的,因为这不符合大家对WebService的一般认知:  使用Attribute声明HTTP方法

ASP.NET WebAPI从入门
       [HttpGet] 
       public IEnumerable<TestUseMode> FindAll() 
       [HttpGet] 
       public IEnumerable<TestUseMode> FindByKey(string key) 
       [HttpPost] 
       public bool Add(TestUseMode mode) 
       [HttpDelete] 
       public int RemoveByKey(string key) 
       [HttpDelete] 
       public int RemoveAll() 
       [HttpPut] 
       public int UpdateByKey(string key, string value) 
       [NonAction]   
       public string GetPrivateData() 
ASP.NET WebAPI从入门

当然,我只列出了方法名,而不是这些方法真的没有方法体...方法体是不变的,NoAction表示这个方法是不接收请求的,即使以GET开头。如果感觉常规的GET,POST,DELETE,PUT不够用,还可以使用AcceptVerbs的方式来声明HTTP方法,如:

ASP.NET WebAPI从入门
[AcceptVerbs("MKCOL", "HEAD")] 
public int UpdateByKey(string key, string value) 
{ 
            List<TestUseMode> upDataList = allModeList.FindAll((mode) => { if (mode.ModeKey == key) return true; return false; }); 
            foreach(var mode in upDataList) 
            { 
                mode.ModeValue = value; 
            } 
            return upDataList.Count; 
} 
ASP.NET WebAPI从入门

附:什么是REST风格? 参考:什么是REST风格  Customers表

ASP.NET WebAPI从入门
Create DataBase NorthWind 
Go 
Use NorthWind 
Go 
CREATE TABLE [dbo].[Customers]( 
    [CustomerID] [nchar](5) NOT NULL, 
    [CompanyName] [nvarchar](40) NOT NULL, 
    [ContactName] [nvarchar](30) NULL, 
    [ContactTitle] [nvarchar](30) NULL, 
    [Address] [nvarchar](60) NULL, 
    [City] [nvarchar](15) NULL, 
    [Region] [nvarchar](15) NULL, 
    [PostalCode] [nvarchar](10) NULL, 
    [Country] [nvarchar](15) NULL, 
    [Phone] [nvarchar](24) NULL, 
    [Fax] [nvarchar](24) NULL, 
CONSTRAINT [PK_Customers] PRIMARY KEY CLUSTERED  
( 
    [CustomerID] ASC 
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY] 
) ON [PRIMARY] 
 
GO 
ASP.NET WebAPI从入门

第二步:创建 FluentData.Entity层,创建Customer实体类 

ASP.NET WebAPI从入门
namespace FluentData.Entity 
{ 
    public class Customer 
    { 
        public string CustomerID { get; set; } 
        public string CompanyName { get; set; } 
        public string ContactName { get; set; } 
        public string ContactTitle { get; set; } 
        public string Address { get; set; } 
        public string City { get; set; } 
        public string Region { get; set; } 
        public string PostalCode { get; set; } 
        public string Country { get; set; } 
        public string Phone { get; set; } 
        public string Fax { get; set; } 
    } 
} 
ASP.NET WebAPI从入门

第三步:利用FluentData做数据的持久化  首先引入FluentData.cs (见附件)  其次:创建DBHelper类,代码如下:

ASP.NET WebAPI从入门
public class DBHelper 
    { 
        public static IDbContext Context() { 
            //return new DbContext().ConnectionString("server=127.0.0.1;uid=sa;pwd=sa;database=TestDB", new SqlServerProvider()); 
            return new DbContext().ConnectionStringName("connString", new SqlServerProvider()); 
        } 
    } 
ASP.NET WebAPI从入门

然后不要忘记修改ASP.NET MVC层所在的Web.config,加入数据库连结字符串:

  <connectionStrings> 
    <add name="connString" connectionString="server=127.0.0.1;database=Northwind;uid=sa;pwd=sa;"/> 
  </connectionStrings>

第三步:创建 CustomerService数据持久化类,代码如下:

ASP.NET WebAPI从入门
public class CustomerService 
    { 
        private IDbContext context = DBHelper.Context(); 
        public Customer Select(string customerId){ 
 
            return context.Select<Customer>("*").From("Customers").Where("CustomerID=@0").Parameters(customerId) 
                   .QuerySingle(); 
             
        } 
        public List<Customer> SelectAll() { 
            return context.Select<Customer>("*").From("Customers").QueryMany(); 
        } 
 
        public List<Customer> SelectAll(string sortExpression) { 
            if (String.IsNullOrEmpty(sortExpression)) return null; 
            return context.Select<Customer>("*").From("Customers").OrderBy(sortExpression).QueryMany(); 
        } 
 
        public List<Customer> SelectAll(int currentPageIndex,int maxRows, string sortExpression) 
        { 
            var select = context.Select<Customer>("*").From("Customers"); 
            if (maxRows > 0) { 
                if (currentPageIndex == 0) currentPageIndex = 1; 
                select.Paging(currentPageIndex, maxRows); 
            } 
            if (!string.IsNullOrEmpty(sortExpression)) { 
                select.OrderBy(sortExpression); 
            } 
 
            return select.QueryMany(); 
        } 
 
        public int CountAll() { 
            return context.Sql("select count(*) from Customers").QuerySingle<int>(); 
        } 
 
        public int Insert(Customer customer) { 
            return context.Insert<Customer>("Customers", customer).Execute(); 
        } 
 
        public int Update(Customer customer) { 
            return context.Update<Customer>("Customers", customer).Where("CustomerID", customer.CustomerID).Execute(); 
        } 
 
        public int Delete(string customerId) { 
            return context.Delete("Customers").Where("CustomerID", customerId).Execute(); 
        } 
 
        public int Delete(Customer customer) 
        { 
            return this.Delete(customer.CustomerID); 
        } 
    } 
ASP.NET WebAPI从入门

第四步:Web API,创建CustomerController  注意要引用:FluentData.Entity及FluentData.DAL 程序集

ASP.NET WebAPI从入门
public class CustomerController : ApiController 
    { 
        private CustomerService customerService = new CustomerService(); 
        //Select All 
        public IEnumerable<Customer> Get() 
        { 
            return customerService.SelectAll(); 
        } 
 
        //Select By Id 
        public Customer Get(string id) 
        { 
            return customerService.Select(id); 
        } 
 
        //Insert 
        public void Post(Customer customer) 
        { 
            customerService.Insert(customer); 
        } 
 
 
        //Update 
        public void Put(string id, Customer obj) 
        { 
            customerService.Update(obj); 
        } 
 
        //Delete 
        public void Delete(string id) 
        { 
            customerService.Delete(id); 
        } 
    } 
ASP.NET WebAPI从入门

第五步:View层代码

ASP.NET WebAPI从入门
namespace MyWebApI.Controllers 
{ 
    public class HomeController : Controller 
    { 
        public ActionResult Index() 
        { 
            return View(); 
        } 
 
        public ActionResult Test() { 
            return View(); 
        } 
 
        public ActionResult CustomerManager() { 
            return View(); 
        } 
    } 
} 
ASP.NET WebAPI从入门

然后创建View 

ASP.NET WebAPI从入门
    <table id="customerTable" border="1" cellpadding="3"  style="700px"> 
        <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"  style="100px" size="5"/></td> 
            <td><input type="text" id="txtCompanyName"  style="150px" /></td> 
            <td><input type="text" id="txtContactName"  style="150px"/></td> 
            <td><input type="text" id="txtCountry"  style="150px"/></td> 
            <td><input type="button" name="btnInsert" value="Insert"  style="150px"/></td> 
        </tr> 
    </table> 
 
<script type="text/javascript"> 
    $(function () { 
        $.getJSON("api/Customer", LoadCustomers); 
    }); 
 
    function LoadCustomers(data) { 
        $("#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="修改" /> <input type="button" name="btnDelete" value="删除" /></td>' + 
                            '</tr>'; 
            $('#customerTable').append(tableRow); 
        }); 
 
        $("input[name='btnInsert']").click(OnInsert); 
        $("input[name='btnUpdate']").click(OnUpdate); 
        $("input[name='btnDelete']").click(OnDelete); 
    } 
 
    function OnInsert(evt) { 
        var customerId = $("#txtCustomerId").val(); 
        var companyName = $("#txtCompanyName").val(); 
        var contactName = $("#txtContactName").val();