ASP.NET Web API和WCF都體現了REST軟體架構風格。在REST中,把一切資料視為資源,所以也是一種面向資源的架構風格。所有的資源都可以通過URI來唯一標識,通過對資源的HTTP操作(GET/HEAD、POST、PUT和DELETE),使資源的表徵狀態發生了改變,即Representational State Transfer,縮寫為REST。
WCF從3.5以來,也體現了REST的架構風格,但對於一般的訊息通訊來說,顯得"過重",所以,微軟推出ASP.NET Web API,提供一種"輕量級"的服務,並且借鑑了MVC,以Controller的形式來定義服務,而Controller中的Action方法對應著不同的HTTP操作。
本篇為"在三層架構下實現SportsStore"系列的第九篇,包括:
■ 11、ASP.NET MVC呼叫ASP.NET Web API的增刪改查服務
□ 11.1 呼叫查詢服務
※ 11.1.1 建立ASP.NET Web API專案 引入Ninject
※ 11.1.2 提供ASP.NET WEB API增刪改查服務
※ 11.1.3 ASP.NET MVC呼叫ASP.NET WEB API查詢服務
11、ASP.NET MVC呼叫ASP.NET Web API提供增刪改查服務
11.1 呼叫查詢服務
11.1.1 建立ASP.NET Web API專案 引入Ninject
在解決方案下建立一個空的"ASP.NET Web API 2空專案",名稱為"MySportsStore.WebApi":
在MySportsStore.WebApi"下新增如下引用:
● MySportsStore.BLL
● MySportsStore.IBLL
● MySportsStore.Model
● 通過GuGet安裝最新版的EntityFramework
● 通過NuGet安裝Ninject
在即將建立的控制器中,會用到IProductService及其實現,我們需要藉助Ninject來管理介面和實現類。在"MySportsStore.WebApi"下,建立一個System.Web.Http.Dependencies.IDependencyResolver介面的類:
using System; using System.Collections.Generic; using System.Web.Http.Dependencies; using Ninject; namespace MySportsStore.WebApi.Extension { public class NinjectDependencyResolver : IDependencyResolver { private List<IDisposable> disposableServices = new List<IDisposable>(); public IKernel Kernel { get; private set; } public NinjectDependencyResolver(NinjectDependencyResolver parent) { this.Kernel = parent.Kernel; } public NinjectDependencyResolver() { this.Kernel = new StandardKernel(); } public void Register<TFrom, TTO>() where TTO : TFrom { this.Kernel.Bind<TFrom>().To<TTO>(); } public IDependencyScope BeginScope() { return new NinjectDependencyResolver(this); } public object GetService(System.Type serviceType) { return this.Kernel.TryGet(serviceType); } public System.Collections.Generic.IEnumerable<object> GetServices(System.Type serviceType) { foreach (var service in this.Kernel.GetAll(serviceType)) { this.AddDisposableService(service); yield return service; } } public void Dispose() { foreach (IDisposable disposable in disposableServices) { disposable.Dispose(); } } private void AddDisposableService(object service) { IDisposable disposable = service as IDisposable; if (null != disposable && !disposableServices.Contains(disposable)) { disposableServices.Add(disposable); } } } }
然後在全域性中註冊:
using System.Web.Http; using MySportsStore.BLL; using MySportsStore.IBLL; using MySportsStore.WebApi.Extension; namespace MySportsStore.WebApi { public class WebApiApplication : System.Web.HttpApplication { protected void Application_Start() { GlobalConfiguration.Configure(WebApiConfig.Register); NinjectDependencyResolver dependencyResolver = new NinjectDependencyResolver(); dependencyResolver.Register<IProductService, ProductService>(); GlobalConfiguration.Configuration.DependencyResolver = dependencyResolver; } } }
11.1.2 提供ASP.NET WEB API增刪改查服務
在"MySportsStore.WebApi"下,建立ProductApi控制器,提供增刪改查服務,實現如下:
using System; using System.Collections.Generic; using System.Linq; using System.Web.Http; using MySportsStore.IBLL; using MySportsStore.Model; using Ninject; namespace MySportsStore.WebApi.Controllers { public class ProductApiController : ApiController { [Inject] public IProductService ProductService { get; set; } public ProductApiController() { this.DisposableObjects = new List<IDisposable>(); this.AddDisposableObject(ProductService); } // GET api/productapi public IEnumerable<Product> Get() { return ProductService.LoadEntities(p => true).AsEnumerable(); } // GET api/productapi/5 public Product Get(int id) { return ProductService.LoadEntities(p => p.Id == id).FirstOrDefault(); } // POST api/productapi public void Post(Product product) { var dbProduct = ProductService.LoadEntities(p => p.Id == product.Id).FirstOrDefault(); ProductService.UpdateEntity(dbProduct); } // PUT api/productapi/5 public void Put(Product product) { ProductService.AddEntity(product); } // DELETE api/productapi/5 public void Delete(int id) { var product = ProductService.LoadEntities(p => p.Id == id).FirstOrDefault(); ProductService.DeleteEntity(product); } #region 手動垃圾回收邏輯 protected IList<IDisposable> DisposableObjects { get; private set; } protected void AddDisposableObject(object obj) { IDisposable disposable = obj as IDisposable; if (disposable != null) { this.DisposableObjects.Add(disposable); } } protected override void Dispose(bool disposing) { if (disposing) { foreach (IDisposable obj in this.DisposableObjects) { if (null != obj) { obj.Dispose(); } } } base.Dispose(disposing); } #endregion } }
以上,通過Ninject把IProductService注入到ProductService屬性上,然後使用ProductService實現增刪改查,並實現了手動垃圾回收。
把"MySportsStore.WebApi"設定為啟動專案,在瀏覽器輸入:http://localhost:1577/api/productapi
以上,說明AP.NET WEB API查詢資料服務是正常的。這裡的地址,也就是代表查詢狀態的唯一URI,通過這個URI,無論你使用何種語言,無論是通過手機客戶端、平板客戶端、電腦客戶端......你都可以呼叫這個REST風格的服務。
11.1.3 ASP.NET MVC呼叫ASP.NET WEB API查詢服務
在MVC中,使用HttpClient來獲取ASP.NET WEB API的服務,以下通過非同步方式查詢來自ASP.NET WEB API的資料:
using System.Collections.Generic; using System.Net.Http; using System.Threading.Tasks; using MySportsStore.Model; using Newtonsoft.Json; namespace MySportsStore.WebUI.RESTServices { public class ProductRESTService { readonly string uri = "http://localhost:1577/api/productapi"; public async Task<List<Product>> GetProductsAsync() { using (HttpClient httpClient = new HttpClient()) { return JsonConvert.DeserializeObject<List<Product>>( await httpClient.GetStringAsync(uri) ); } } } }
在"MySportsStore.WebUI"下,建立Home控制器:
using System.Threading.Tasks; using System.Web.Mvc; using MySportsStore.WebUI.RESTServices; namespace MySportsStore.WebUI.Controllers { public class HomeController : Controller { private ProductRESTService service = new ProductRESTService(); public async Task<ActionResult> Index() { return View("Index", await service.GetProductsAsync() ); } } }
在"MySportsStore.WebUI"下,建立Home/Index.cshtml檢視:
@model IEnumerable<MySportsStore.Model.Product> @{ Layout = null; } <!DOCTYPE html> <html> <head> <meta name="viewport" content="width=device-width" /> <title>Index</title> <script src="~/Scripts/jquery-1.8.2.js"></script> <style type="text/css"> table { width: 1000px; border: 1px solid #000000; background-color: #eee; } table tr { line-height: 20px; border-bottom: 1px solid black; } table th { background-color: #ccc; color: #fff; } .oddRow { background-color: #fff; } #products { border: 0px solid red; } </style> </head> <body> <div id="products"> <table> <tr class="oddRow"> <th>編號</th> <th>類別</th> <th>名稱</th> <th>價格</th> <th>描述</th> </tr> @foreach (var product in Model) { <tr> <td>@product.Id</td> <td>@product.Category</td> <td>@product.Name</td> <td>@product.Price.ToString("c")</td> <td>@product.Description</td> </tr> } </table> </div> </body> </html>
把"MySportsStore.WebUI"設定為啟動專案,執行:
至此,ASP.NET MVC呼叫ASP.NET Web API的查詢服務結束。
參考資料:
引入Ninject部分,參考了蔣金楠(Artech)的"IoC在ASP.NET Web API中的應用"
原始碼在這裡。
“MVC專案實踐,在三層架構下實現SportsStore”系列包括: