1.WebApi是什麼

ASP.NET Web API 是一種框架,用於輕鬆構建可以由多種客戶端(包括瀏覽器和移動裝置)訪問的 HTTP 服務。ASP.NET Web API 是一種用於在 .NET Framework 上構建 RESTful 應用程式的理想平臺。

可以把WebApi看成Asp.Net專案型別中的一種,其他專案型別諸如我們熟知的WebForm專案,Windows窗體專案,控制檯應用程式等。

WebApi型別專案的最大優勢就是,開發者再也不用擔心客戶端和伺服器之間傳輸的資料的序列化和反序列化問題,因為WebApi是強型別的,可以自動進行序列化和反序列化,一會兒專案中會見到。

下面我們建立了一個WebApi型別的專案,專案中對產品Product進行增刪改查,Product的資料存放在List<>列表(即記憶體)中。

2.頁面執行效果

如圖所示,可以新增一條記錄; 輸入記錄的Id,查詢出該記錄的其它資訊; 修改該Id的記錄; 刪除該Id的記錄。

3.二話不說,開始建專案

1)新建一個“ASP.NET MVC 4 Web 應用程式”專案,命名為“ProductStore”,點選確定,如圖

2)選擇模板“Web API”,點選確定,如圖

3)和MVC型別的專案相似,構建程式的過程是先建立資料模型(Model)用於存取資料, 再建立控制器層(Controller)用於處理髮來的Http請求,最後構造顯示層(View)用於接收使用者的輸入和使用者進行直接互動。

這裡我們先在Models資料夾中建立產品Product類: Product.cs,如下:

  1. using System;
  2. using System.Collections.Generic;
  3. using System.Linq;
  4. using System.Web;
  5.  
  6. namespace ProductStore.Models
  7. {
  8. public class Product
  9. {
  10. public int Id { get; set; }
  11. public string Name { get; set; }
  12. public string Category { get; set; }
  13. public decimal Price { get; set; }
  14. }
  15. }

4)試想,我們目前只有一個Product型別的物件,我們要編寫一個類對其實現增刪改查,以後我們可能會增加其他的型別的物件,再需要編寫一個對新型別的物件進行增刪改查的類,為了便於拓展和呼叫,我們在Product之上構造一個介面,使這個介面約定增刪改查的方法名稱和引數,所以我們在Models資料夾中新建一個介面:  IProductRepository.cs ,如下:

  1. using System;
  2. using System.Collections.Generic;
  3. using System.Linq;
  4. using System.Text;
  5. using System.Threading.Tasks;
  6.  
  7. namespace ProductStore.Models
  8. {
  9. interface IProductRepository
  10. {
  11. IEnumerable<Product> GetAll();
  12. Product Get(int id);
  13. Product Add(Product item);
  14. void Remove(int id);
  15. bool Update(Product item);
  16. }
  17. }

5)然後,我們實現這個介面,在Models資料夾中新建一個類,具體針對Product型別的物件進行增刪改查存取資料,並在該類的構造方法中,向List<Product>列表中存入幾條資料,這個類叫:ProductRepository.cs,如下:

  1. using System;
  2. using System.Collections.Generic;
  3. using System.Linq;
  4. using System.Web;
  5.  
  6. namespace ProductStore.Models
  7. {
  8. public class ProductRepository:IProductRepository
  9. {
  10. private List<Product> products = new List<Product>();
  11. private int _nextId = ;
  12. public ProductRepository()
  13. {
  14. Add(new Product { Name="Tomato soup",Category="Groceries",Price=1.39M});
  15. Add(new Product { Name="Yo-yo",Category="Toys",Price=3.75M});
  16. Add(new Product { Name = "Hammer", Category = "Hardware", Price = 16.99M });
  17. }
  18.  
  19. public IEnumerable<Product> GetAll()
  20. {
  21. return products;
  22. }
  23.  
  24. public Product Get(int id)
  25. {
  26. return products.Find(p=>p.Id==id);
  27. }
  28.  
  29. public Product Add(Product item)
  30. {
  31. if (item == null)
  32. {
  33. throw new ArgumentNullException("item");
  34. }
  35. item.Id = _nextId++;
  36. products.Add(item);
  37. return item;
  38. }
  39.  
  40. public void Remove(int id)
  41. {
  42. products.RemoveAll(p=>p.Id==id);
  43. }
  44.  
  45. public bool Update(Product item)
  46. {
  47. if (item == null)
  48. {
  49. throw new ArgumentNullException("item");
  50. }
  51. int index = products.FindIndex(p=>p.Id==item.Id);
  52. if (index == -)
  53. {
  54. return false;
  55. }
  56. products.RemoveAt(index);
  57. products.Add(item);
  58. return true;
  59. }
  60. }
  61. }

此時,Model層就構建好了。

6)下面,我們要構建Controller層,在此之前,先回顧一下Http中幾種請求型別,如下

get  型別  用於從伺服器端獲取資料,且不應該對伺服器端有任何操作和影響

post 型別  用於傳送資料到伺服器端,建立一條新的資料,對伺服器端產生影響

put 型別  用於向伺服器端更新一條資料,對伺服器端產生影響 (也可建立一條新的資料但不推薦這樣用)

delete 型別 用於刪除一條資料,對伺服器端產生影響

這樣,四種請求型別剛好可對應於對資料的 查詢,新增,修改,刪除。WebApi也推薦如此使用。在WebApi專案中,我們請求的不再是一個具體頁面,而是各個控制器中的方法(控制器也是一種類,預設放在Controllers資料夾中)。下面我們將要建立一個ProductController.cs控制器類,其中的方法都是以“Get Post Put Delete”中的任一一個開頭的,這樣的開頭使得Get型別的請求傳送給以Get開頭的方法去處理,Post型別的請求交給Post開頭的方法去處理,Put和Delete同理。

而以Get開頭的方法有好幾個也是可以的,此時如何區分到底交給哪個方法執行呢?這就取決於Get開頭的方法們的傳入引數了,一會兒在程式碼中可以分辨。
   構建Controller層,在Controllers資料夾中建立一個ProductController.cs控制器類,如下:

  1. using System;
  2. using System.Collections.Generic;
  3. using System.Linq;
  4. using System.Web;
  5. using System.Web.Mvc;
  6. using ProductStore.Models;
  7. using System.Web.Http;
  8. using System.Net;
  9. using System.Net.Http;
  10.  
  11. namespace ProductStore.Controllers
  12. {
  13. public class ProductsController : ApiController
  14. {
  15. static readonly IProductRepository repository = new ProductRepository();
  16.  
  17. //GET: /api/products
  18. public IEnumerable<Product> GetAllProducts()
  19. {
  20. return repository.GetAll();
  21. }
  22.  
  23. //GET: /api/products/id
  24. public Product GetProduct(int id)
  25. {
  26. Product item = repository.Get(id);
  27. if (item == null)
  28. {
  29. throw new HttpResponseException(HttpStatusCode.NotFound);
  30. }
  31. return item;
  32. }
  33.  
  34. //GET: /api/products?category=category
  35. public IEnumerable<Product> GetProductsByCategory(string category)
  36. {
  37. return repository.GetAll().Where(p=>string.Equals(p.Category,category,StringComparison.OrdinalIgnoreCase));
  38. }
  39.  
  40. //POST: /api/products
  41. public HttpResponseMessage PostProduct(Product item)
  42. {
  43. item = repository.Add(item);
  44.  
  45. var response = Request.CreateResponse<Product>(HttpStatusCode.Created,item);
  46. string uri = Url.Link("DefaultApi", new { id=item.Id});
  47. response.Headers.Location = new Uri(uri);
  48.  
  49. return response;
  50. }
  51.  
  52. //PUT: /api/products/id
  53. public void PutProduct(int id, Product product)
  54. {
  55. product.Id = id;
  56. if (!repository.Update(product))
  57. {
  58. throw new HttpResponseException(HttpStatusCode.NotFound);
  59. }
  60. }
  61.  
  62. //Delete: /api/products/id
  63. public void DeleteProduct(int id)
  64. {
  65. Product item = repository.Get(id);
  66. if (item == null)
  67. {
  68. throw new HttpResponseException(HttpStatusCode.NotFound);
  69. }
  70. repository.Remove(id);
  71. }
  72. }
  73. }

使該類繼承於ApiController類,在其中實現處理各種Get,Post,Put,Delete型別Http請求的方法。

每一個方法前都有一句註釋,標識了該方法的針對的請求的型別(取決於方法的開頭),以及要請求到該方法,需要使用的url。

這些url是有規律的,見下圖:

api是必須的,products對應的是ProductsControllers控制器,然後又Http請求的型別和url的後邊地址決定。

這裡,我們除了第三個“Get a product by category”,其他方法都實現了。

7)最後,我們來構建View檢視層,我們更改Views資料夾中的Home資料夾下的Index.cshtml檔案,這個檔案是專案啟動頁,如下:

  1. <div id="body">
  2. <script src="~/Scripts/jquery-1.8.2.min.js"></script>
  3. <section >
  4.   <h2>新增記錄</h2>
  5.   Name:<input id="name" type="text" /><br />
  6.   Category:<input id="category" type="text" />
  7.   Price:<input id="price" type="text" /><br />
  8.   <input id="addItem" type="button" value="新增" />
  9. </section>
  10.  
  11. <section>
  12. <br />
  13. <br />
  14.   <h2>修改記錄</h2>
  15.   Id:<input id="id2" type="text" /><br />
  16.   Name:<input id="name2" type="text" /><br />
  17.   Category:<input id="category2" type="text" />
  18.   Price:<input id="price2" type="text" /><br />
  19.   <input id="showItem" type="button" value="查詢" />
  20.   <input id="editItem" type="button" value="修改" />
  21.   <input id="removeItem" type="button" value="刪除" />
  22. </section>
  23.  
  24. </div>

8)然後我們給頁面新增js程式碼,對應上面的按鈕事件,用來發起Http請求,如下:

  1. <script>
  2. //用於儲存使用者輸入資料
  3. var Product = {
  4. create: function () {
  5. Id: "";
  6. Name: "";
  7. Category: "";
  8. Price: "";
  9. return Product;
  10. }
  11. }
  12.  
  13. //新增一條記錄 請求型別:POST 請求url: /api/Products
  14. //請求到ProductsController.cs中的 public HttpResponseMessage PostProduct(Product item) 方法
  15. $("#addItem").click(function () {
  16. var newProduct = Product.create();
  17. newProduct.Name = $("#name").val();
  18. newProduct.Category = $("#category").val();
  19. newProduct.Price = $("#price").val();
  20.  
  21. $.ajax({
  22. url: "/api/Products",
  23. type: "POST",
  24. contentType: "application/json; charset=utf-8",
  25. data: JSON.stringify(newProduct),
  26. success: function () {
  27. alert("新增成功!");
  28. },
  29. error: function (XMLHttpRequest, textStatus, errorThrown) {
  30. alert("請求失敗,訊息:" + textStatus + " " + errorThrown);
  31. }
  32. });
  33. });
  34.  
  35. //先根據Id查詢記錄 請求型別:GET 請求url: /api/Products/Id
  36. //請求到ProductsController.cs中的 public Product GetProduct(int id) 方法
  37. $("#showItem").click(function () {
  38. var inputId = $("#id2").val();
  39. $("#name2").val("");
  40. $("#category2").val("");
  41. $("#price2").val("");
  42. $.ajax({
  43. url: "/api/Products/" + inputId,
  44. type: "GET",
  45. contentType: "application/json; charset=urf-8",
  46. success: function (data) {
  47. $("#name2").val(data.Name);
  48. $("#category2").val(data.Category);
  49. $("#price2").val(data.Price);
  50. },
  51. error: function (XMLHttpRequest, textStatus, errorThrown) {
  52. alert("請求失敗,訊息:" + textStatus + " " + errorThrown);
  53. }
  54. });
  55. });
  56.  
  57. //修改該Id的記錄 請求型別:PUT 請求url: /api/Products/Id
  58. //請求到ProductsController.cs中的 public void PutProduct(int id, Product product) 方法
  59. $("#editItem").click(function () {
  60. var inputId = $("#id2").val();
  61. var newProduct = Product.create();
  62. newProduct.Name = $("#name2").val();
  63. newProduct.Category = $("#category2").val();
  64. newProduct.Price = $("#price2").val();
  65.  
  66. $.ajax({
  67. url: "/api/Products/" + inputId,
  68. type: "PUT",
  69. data: JSON.stringify(newProduct),
  70. contentType: "application/json; charset=urf-8",
  71. success: function () {
  72. alert("修改成功! ");
  73. },
  74. error: function (XMLHttpRequest, textStatus, errorThrown) {
  75. alert("請求失敗,訊息:" + textStatus + " " + errorThrown);
  76. }
  77. });
  78. });
  79.  
  80. //刪除輸入Id的記錄 請求型別:DELETE 請求url: /api/Products/Id
  81. //請求到ProductsController.cs中的 public void DeleteProduct(int id) 方法
  82. $("#removeItem").click(function () {
  83. var inputId = $("#id2").val();
  84. $.ajax({
  85. url: "/api/Products/" + inputId,
  86. type: "DELETE",
  87. contentType: "application/json; charset=uft-8",
  88. success: function (data) {
  89. alert("Id為 " + inputId + " 的記錄刪除成功!");
  90. },
  91. error: function (XMLHttpRequest, textStatus, errorThrown) {
  92. alert("請求失敗,訊息:" + textStatus + " " + errorThrown);
  93. }
  94. });
  95. });
  96. </script>

這裡,WebApi的一個簡單的增刪改查專案就完成了,選擇執行專案即可測試。注意到,其中用ajax發起請求時,傳送到伺服器端的資料直接是一個json字串,當然這個json字串中每個欄位要和Product.cs類中的每個欄位同名對應,在伺服器端接收資料的時候,我們並沒有對接收到的資料進行序列化,而返回資料給客戶端的時候也並沒有對資料進行反序列化,大大節省了以前開發中不停地進行序列化和反序列化的時間。