實例快速上手 -ASP.NET 4.5新特性WebAPI從入門到精通


在新出的MVC4中,增加了WebAPI,用於提供REST風格的WebService,新生成的WebAPI項目和典型的MVC項目一樣,包含主要的ModelsViewsControllers等文件夾和Global.asax文件。Views對於WebAPI來說沒有太大的用途,Models中的Model主要用於保存ServiceClient交互的對象,這些對象默認情況下會被轉換為Json格式的數據迚行傳輸,Controllers中的Controller對應於WebService來說是一個Resource,用於提供服務。和普通的MVC一樣,Global.asax用於配置路由規則。 
環境准備 
     建議使用VS2012以上版本創建WebAPI,如果是使用VS2010,需要安裝VS2010 SP1升級包,MVC4升級包,打開VS2012創建如下: 
第一步:新建ASP.NET Web應用程序

第二步:建議WebAPI

新生成的WebAPI項目和典型的MVC項目一樣,包含主要的ModelsViewsControllers等文件夾和Global.asax文件

注意:再次強調Views對於WebAPI來說沒有太大的用途,Models中的Model主要用於保存ServiceClient交互的對象,這些對象默認情況下會被轉換為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的屬性,用於jsonxml反序列化時的賦值 
(三)Controllers 
MVC WebAPI中的Controllers和普通MVCControllers類似,不過不再繼承於Controller,而改為繼承APIApiController,一個Controller可以包含多個Action,這些Action響應請求的方法與Global中配置的路由規則有關,在后面結束Global時統一說明 
 
(四)Global 
默認情況下,模板自帶了兩個路由規則,分別對應於WebAPI和普通MVCWeb請求,默認的WebAPI路由規則如下 

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

可以看到,默認路由使用的固定的api作為Uri的先導,按照微軟官方的說法,用於區分普通Web請求和WebService的請求路徑: 
可以看到,默認的路由規則只指向了Controller,沒有指向具體的Action,因為默認情況下,對於Controller中的Action的匹配是和Action的方法名相關聯的:具體來說,如果使用上面的路由規則,對應下面的Controller

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; 
        } 
} 

則,會有下面的對應關系:

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調用

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); 
        }); 
    } 

這樣就實現了最基本的CRUD操作。 
擴展需求 
問題1:我想按照用戶名稱(UserName)進行查詢,怎么辦? 
辦法:第一步:在UserController類中加一個方法名稱叫:GetUserByName,如下所示: 

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

第二步:在客戶端index.cshtml中調用 

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); 
        }); 
   } 

如果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); }); 
        } 

第二步:客戶端調用:

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); 
        }); 
   } 

路由規則擴展 
和普通的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}" 
這樣,就要求同時使用ActionHTTP方法進行匹配當然,根據微軟的說法,這種使用是不被推薦的,因為這不符合大家對WebService的一般認知: 
使用Attribute聲明HTTP方法

       [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() 

當然,我只列出了方法名,而不是這些方法真的沒有方法體...方法體是不變的,NoAction表示這個方法是不接收請求的,即使以GET開頭。如果感覺常規的GETPOSTDELETEPUT不夠用,還可以使用AcceptVerbs的方式來聲明HTTP方法,如:

[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; 
} 

附:什么是REST風格? 參考:什么是REST風格 
http://hi.baidu.com/yankaiwei/item/1f0b37dd922d53ef3cc2cb69 
第二部分:綜合示例:應用ASP.NET MVC4+WebAPI+FluentData開發Web應用 
第一步:創建數據庫 
NorthWind數據庫的Customers

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 

第二步:創建 FluentData.Entity層,創建Customer實體類 

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; } 
    } 
} 

第三步:利用FluentData做數據的持久化 
首先引入FluentData.cs (見附件) 
其次:創建DBHelper類,代碼如下:

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 MVC層所在的Web.config,加入數據庫連結字符串:

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

第三步:創建 CustomerService數據持久化類,代碼如下:

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); 
        } 
    } 

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

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); 
        } 
    } 

第五步:View層代碼

namespace MyWebApI.Controllers 
{ 
    public class HomeController : Controller 
    { 
        public ActionResult Index() 
        { 
            return View(); 
        } 
 
        public ActionResult Test() { 
            return View(); 
        } 
 
        public ActionResult CustomerManager() { 
            return View(); 
        } 
    } 
} 

然后創建View 

    <table id="customerTable" border="1" cellpadding="3"  style="width: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="width:100px" size="5"/></td> 
            <td><input type="text" id="txtCompanyName"  style="width:150px" /></td> 
            <td><input type="text" id="txtContactName"  style="width:150px"/></td> 
            <td><input type="text" id="txtCountry"  style="width:150px"/></td> 
            <td><input type="button" name="btnInsert" value="Insert"  style="width: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(); 
        var country = $("#txtCountry").val(); 
        var data = '{"CustomerID":"' + customerId + '","CompanyName":"' + companyName + '","ContactName":"' + contactName + '","Country":"' + country + '"}'; 
 
        $.ajax({ 
            type: 'POST', 
            url: '/api/Customer/', 
            data: data, 
            contentType: "application/json; charset=utf-8", 
            dataType: 'json', 
            success: function (results) { 
                $("#txtCustomerId").val(''); 
                $("#txtCompanyName").val(''); 
                $("#txtContactName").val(''); 
                $("#txtCountry").val(''); 
                $.getJSON("api/customers" + new Date().getTime(), LoadCustomers); 
                alert('添加成功!'); 
            } 
        }).fail( 
        function (xhr, textStatus, err) { 
            alert('添加失敗,原因如下: ' + err); 
        }); 
    } 
    function OnUpdate(evt) { 
 
        var input; 
        var customerId = $(this).parent().parent().children().get(0).innerHTML; 
 
        input = $($(this).parent().parent().children().get(1)).find("input"); 
        //input.removeAttr("disabled"); 
        var companyName = input.val(); 
 
        input = $($(this).parent().parent().children().get(2)).find("input"); 
        //input.removeAttr("disabled"); 
        var contactName = input.val(); 
 
        input = $($(this).parent().parent().children().get(3)).find("input"); 
        //input.removeAttr("disabled"); 
        var country = input.val(); 
 
        var data = '{"CustomerID":"' + customerId + '","CompanyName":"' + companyName + '","ContactName":"' + contactName + '","Country":"' + country + '"}'; 
         
        $.ajax({ 
            type: 'PUT', 
            url: '/api/Customer/' + customerId, 
            data: data, 
            contentType: "application/json; charset=utf-8", 
            dataType: 'json', 
            success: function (results) { 
                $.getJSON("api/Customer" + new Date().getTime(), LoadCustomers); 
                alert('修改成功 !'); 
            } 
        }).fail( 
        function (xhr, textStatus, err) { 
            alert('修改失敗,原因如下: ' + err); 
        }); 
    } 
    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/Customer/' + customerId, 
            contentType: "application/json; charset=utf-8", 
            dataType: 'json', 
            success: function (results) { 
                $.getJSON("api/Customer?"+new Date().getTime(), LoadCustomers); 
                alert('成功刪除!'); 
            } 
        }).fail( 
        function (xhr, textStatus, err) { 
            alert('刪除失敗,原因如下: ' + err); 
        }); 
 
    } 
</script> 

第三部分:Web API高級部分 
在第一部分和大家一起學習了建立基本的WebAPI應用,第二部分寫了一個綜合示例,立刻就有人想到了一些問題:1.客戶端和WebService/WebAPI之間文件傳輸2.客戶端或者服務端的安全控制要解決這些問題,要了解一下WebAPI的基本工作方式。 
(一)WebAPI中工作的Class 
MVC中大家都知道,獲取RequestResponse使用HttpRequestHttpResponse兩個類,在WebAPI中使用兩外兩個類:HttpRequestMessageHttpResponseMessage,分別用於封裝RequsetResponse。除了這兩個類之外,還有一個常見的抽象 類:HttpMessageHandler,用於過濾和加工HttpRequestMessageHttpResponseMessage 
 
(二)解決第一個問題:客戶端和WebService之間文件傳輸其 實第一個問題之所以被提出來應該是和客戶端有關,如果客戶端的請求是我們手寫提交的,比如使用HttpClient封裝的請求,則要傳遞文件之前,我們一 般會進行一次序列化,轉化為二進制數組之類的,在網絡上傳輸。這樣的話,在Controller中的Action參數里,我們只需要接收這個二進制數組類 型的對象就可以了。但是如果客戶端是Web Form呢,比如我們提交一個Form到指定的ControllerAction中,這個Action要接收什么類型的參數呢?或者我們問另外一個問題,如果我將Web Form提交到一個WebAPIAction中 ,我要怎么去取出這個表單中的數據呢?其 實我們應該想到:我們的Action設置的參數之所以能夠被賦值,是因為WebAPI的架構中在調用Action時將HTTP請求中的數據解析出來分別賦 值給Action中的參數,如果真是這樣的話,我們只需要在Action中獲取到HTTP請求,然后直接獲取請求里面的數據,就能解決上面的問題。這 種想法是正確的,只不過,此時的HTTP請求已經不是最原始的HTTP Request,而是已經被轉化成了HttpRequestMessage,在Action中,我們可以直接調用base.Requet來得到這個 HttpRequestMessage實例,通過這個實例我們就可以隨心所欲的取出HTTP請求中想要的數據 
 
2.1RequestMessage中獲取普通表單數據 
這里的普通表單是指不包含File的表單,也就是說表單的enctype值不是multipart/form-data,這時,表單的數據默認情況下是以Json來傳遞的如下頁面

<form name="form" action="~/api/File/Register" method="post"> 
    <input type="text" name="userName" /> 
    <br /> 
    <input type="text" name="passWord"  /> 
    <br />     
    <input type="submit" value="Submit" />  
</form>

ApiContoller:

  public class FileController : ApiController 
  { 
        [HttpPost] 
        public async Task  Register() 
        { 
              HttpContent content = Request.Content; 
             var jsonValue = await content.ReadAsStringAsync(); 
            Console.WriteLine(jsonValue); 
        } 
  }

執行:userName字段輸入admin,passWord字段輸入123456 
傳遞到服務器端輸出的是:userName=admin&passWord=12345 
2.2RequestMessage中獲取multipart表單數據將view頁面改寫為:

<form name="form" action="~/api/File/SubmitFile" method="post" enctype="multipart/form-data" > 
    <input type="text" name="userName2"  /> 
    <br /> 
    <input type="text" name="passWord2" /> 
    <br /> 
    <input type="file" name="file" id="upFile" /> 
    <br /> 
    <input type="submit" value="Submit" /> 
</form>

ApiController代碼:

        public async Task<string> SubmitFile() 
        { 
            // 檢查是否是 multipart/form-data 
            if (!Request.Content.IsMimeMultipartContent("form-data")) 
                throw new HttpResponseException(HttpStatusCode.UnsupportedMediaType); 
            var path = HttpContext.Current.Server.MapPath("~/File"); 
            // 設置上傳目錄 
            var provider = new MultipartFormDataStreamProvider(path); 
            // 接收數據,並保存文件 
            var bodyparts = await Request.Content.ReadAsMultipartAsync(provider); 
            var file = provider.FileData[0];//provider.FormData  
            string orfilename = file.Headers.ContentDisposition.FileName.TrimStart('"').TrimEnd('"'); 
            FileInfo fileinfo = new FileInfo(file.LocalFileName);   
            String ymd = DateTime.Now.ToString("yyyyMMdd", System.Globalization.DateTimeFormatInfo.InvariantInfo); 
            String newFileName = DateTime.Now.ToString("yyyyMMddHHmmss_ffff", System.Globalization.DateTimeFormatInfo.InvariantInfo); 
            string fileExt = orfilename.Substring(orfilename.LastIndexOf('.')); 
            fileinfo.CopyTo(Path.Combine(path, newFileName + fileExt), true); 
            fileinfo.Delete();  
            string result = ""; 
            // 獲取表單數據 
            result += "formData userName: " + bodyparts.FormData["userName1"]; 
            result += "<br />"; 
            // 獲取文件數據 
            result += "fileData headers: " + bodyparts.FileData[0].Headers; // 上傳文件相關的頭信息 
            result += "<br />"; 
            result += "fileData localFileName: " + bodyparts.FileData[0].LocalFileName; // 文件在服務端的保存地址,需要的話自行 rename 或 move 
            return result; 
        } 

還有一種簡單的方式

public string Post()  
         { 
                HttpPostedFile file = HttpContext.Current.Request.Files[0]; 
                string strPath = "D:\\MyProjects\\StudySolution\\RestDemo\\Upload\\test2.rar" ; 
                file.SaveAs(strPath); 
                string result = "0"; 
                return result; 
          } 

注:上述的文件上傳代碼涉及到asyncTaskawait 這些關鍵字是 asp.net mvc 中異步編程的知識點,在這里暫不做過多解釋,不了解的同學可以去先了解一下這塊的內容,后期在我的系列主題文章中也還會有這塊知識點的講解,敬請關注! 
解決第二個問題:客戶端或者服務端的安全控制 
WebAPI的工作方式:HTTP的請求最先是被傳遞到HOST中的,如果WebAPI是被寄宿在IIS上的,這個HOST就是IIS上,HOST是沒有能力也沒有必要進行請求的處理的,請求通過HOST被轉發給了HttPServer此時已經進入WebAPI的處理加工范圍,HttpServerSystem.Net.HTTP中的一個類,通過HttpServer,請求被封裝成了WebAPI中的請求承載 類:HttpRequestMessage,這個封裝后的請求可以經過一系列自定義的Handler來處理,這些handler串聯成一個 pipeline,最后請求會被傳遞給HttpControlDispather,這個類通過對路由表的檢索來確定請求將被轉發到的具體的 Controller中的Action。 
   由此我們早就可以看出,想要解決第二個問題,可以直接在Handler PipeLine中進行,這種AOP風格的過濾器(攔截器)在RESTWebservice的安全驗證中應用很廣,一般大家比較樂於在HTTP頭或者在 HTTP請求的URL中加上身份驗證字段進行身份驗證,下面舉一個在Http頭中添加身份驗證信息的小例子: 
3.1客戶端客戶端的customhandler用於將身份驗證信息添加入報頭

class RequestCheckHandler : DelegatingHandler 
{ 
        protected override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, System.Threading.CancellationToken cancellationToken) 
        { 
            request.Headers.Add("keyword", "ibeifeng"); 
            return base.SendAsync(request, cancellationToken); 
        } 
} 

注:1.customhandler繼承自DelegatingHandler類,上面已經說過,WebAPI的客戶端和服務端被設計為相互對應的兩套結構,所以不論是在客戶端還是服務端,customhandler都是繼承自DelegatingHandler2.DelegatingHandlersendAsync方法便是處理請求和接受請求時會被調用的方法,該方法返回值是HttPResponseMessage,接收的值為HttpRequestMessage,符合我們的一般認知3.方法的最后,調用base.SendAsync是將Request繼續向該pipeline的其他customHandler傳遞,並獲取其返回值。由於該方法不包含Response的處理邏輯,只需直接將上一個CustomHandler的返回值直接返回 
 
客戶端主程序: 

static void Main(string[] args) 
{ 
           HttpClient client = new HttpClient(new RequestCheckHandler() { InnerHandler = new HttpClientHandler() }); 
            HttpResponseMessage response = client.GetAsync("http://localhost:47673/api/File/GetUserInfo?userName=admin").Result; 
            response.Content.ReadAsStringAsync().ContinueWith((str) => { Console.WriteLine(str.Result); }); 
} 

客戶端的主程序創建了一個HttpClientHttpClient可以接受一個參數,該參數就是CustomHandler,此處我們嵌入了我們定義的 RequestUpHandler,用於對Request報頭進行嵌入身份驗證碼的處理,CustomHandler通過InnerHandler屬性嵌 入其內置的下一個CustomHandler,此處,由於沒有下一個CustomerHandler,我們直接嵌入HttpClientHandler用 於將HttpRequestMessage轉化為HTTP 請求、將HTTP響應轉化為HttpResponseMessage 
 
3.2服務端服務端的customHandler用於解析HTTP報頭中的身份認證碼 

public class SecurityHandler : DelegatingHandler 
{ 
   protected override System.Threading.Tasks.Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, System.Threading.CancellationToken  
      cancellationToken) 
    { 
            int matchHeaderCount = request.Headers.Count((item) => 
            { 
                if ("keyword".Equals(item.Key)) 
                { 
                    foreach (var str in item.Value) 
                    { 
                        if ("ibeifeng".Equals(str)) 
                        { 
                            return true; 
                        } 
                    } 
                } 
                return false; 
            }); 
            if (matchHeaderCount>0) 
            { 
                return base.SendAsync(request, cancellationToken); 
            } 
            return Task.Factory.StartNew<HttpResponseMessage>(() => { return new HttpResponseMessage(HttpStatusCode.Forbidden); }); 
   } 
} 

另: 
FileController中加一個測試方法:

        [HttpGet] 
        public string GetUserInfo(string userName) 
        { 
            if (userName == "admin") 
            { 
                return "success"; 
            } 
            else { 
                return "failed"; 
            }  
        }

注:代碼的處理邏輯很簡單:如果身份驗證碼匹配成功,則通過base.SendAsync繼續將請求向下傳遞,否則返回直接中斷請求的傳遞,直接返回一個響應碼為403的響應,指示沒有權限。注意由於SendAsync的返回值需要封裝在Task之中,所以需要使用Task.Factory.StartNew將返回值包含在TaskcustomHandler注入到HOST中本例中WebAPI HOSTIIS上,所以我們只需將我們定義的CustomHandlerApplication_Start中定義即可 

protected void Application_Start() 
        { 
            //省略其他邏輯代碼 
            GlobalConfiguration.Configuration.MessageHandlers.Add(new SecurityHandler ()); 
        } 

由於WebAPI HostIIS上,所以HttpServerHttpControllerDispatcher不用我們手工處理 
在加上上面的處理后,如果沒有身份驗證碼的請求,會得到如下的響應 
 
 
總結 
1.使用WebAPI的目的 
  當你遇到以下這些情況的時候,就可以考慮使用Web API了。 
  a. 需要Web Service但是不需要SOAP 
  b. 需要在已有的WCF服務基礎上建立non-soap-based http服務 
  c. 只想發布一些簡單的Http服務,不想使用相對復雜的WCF配置 
  d. 發布的服務可能會被帶寬受限的設備訪問 
  e. 希望使用開源框架,關鍵時候可以自己調試或者自定義一下框架 
2.使用WebAPI的幾種方式 與注意事項 
3.使用WebAPI實現文件上傳 
4.如何加強WebAPI的安全性 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM