今天來為大家介紹如何在 ASP.NET MVC 中集成 AngularJS 的最后一部分內容。
調試路由表 - HTML 緩存清除
就在我以為示例應用程序完成之后,我意識到,我必須提供兩個版本的路由表:一個運行在調試模式的應用程序下和一個運行在發布模式的應用程序下。在調試模式下,JavaScript 文件在未使用壓縮功能的情況下會被下載。如果想要調試並在 JavaScript 控制器中設置斷點,這是必須的。事實上,路由表的產生版本也出現了一些挑戰,由於產生路由代碼使用的是 JavaScript 捆綁,但是在 Visual Studio 下,捆綁無法一步一步執行調試,所以我無法調試這些代碼。我不得不將一些 console.log 命令和一些 JavaScript 語句警報一起開發並測試來生成路由表。
兩個路由版本都包含的事情是:支持 HTML 文件的緩存,就像捆綁和 JavaScript,你還需要提供一個附屬在 HTML Angular 視圖上的序列號。在調試和生成路由代碼兩種情況下,嵌入版本號將會從 applicationConfigurationProvder 中推出並附屬在緩存的 HTML 路徑中。
// CodeProjectRouting-debug.js
angular.module("codeProject").config( ['$routeProvider', '$locationProvider', 'applicationConfigurationProvider', function ($routeProvider, $locationProvider, applicationConfigurationProvider) { this.getApplicationVersion = function () { var applicationVersion = applicationConfigurationProvider.getVersion(); return applicationVersion; } var baseSiteUrlPath = $("base").first().attr("href"); $routeProvider.when('/:section/:tree', { templateUrl: function (rp) { return baseSiteUrlPath + 'views/' + rp.section + '/' + rp.tree + '.html?v=' + this.getApplicationVersion(); }, resolve: { load: ['$q', '$rootScope', '$location', function ($q, $rootScope, $location) { var path = $location.path().split("/"); var directory = path[1]; var controllerName = path[2]; var controllerToLoad = "Views/" + directory + "/" + controllerName + "Controller.js?v=" + this.getApplicationVersion(); var deferred = $q.defer(); require([controllerToLoad], function () { $rootScope.$apply(function () { deferred.resolve(); }); }); return deferred.promise; }] } }); $routeProvider.when('/:section/:tree/:id', { templateUrl: function (rp) { return baseSiteUrlPath + 'views/' + rp.section + '/' + rp.tree + '.html?v=' + this.getApplicationVersion(); }, resolve: { load: ['$q', '$rootScope', '$location', function ($q, $rootScope, $location) { var path = $location.path().split("/"); var directory = path[1]; var controllerName = path[2]; var controllerToLoad = "Views/" + directory + "/" + controllerName +
"Controller.js?v=" + this.getApplicationVersion(); var deferred = $q.defer(); require([controllerToLoad], function () { $rootScope.$apply(function () { deferred.resolve(); }); }); return deferred.promise; }] } }); $routeProvider.when('/', { templateUrl: function (rp) { return baseSiteUrlPath + 'views/Home/Index.html?v=' +
this.getApplicationVersion(); }, resolve: { load: ['$q', '$rootScope', '$location', function ($q, $rootScope, $location) { var controllerToLoad = "Views/Home/IndexController.js?v=" +
this.getApplicationVersion(); var deferred = $q.defer(); require([controllerToLoad], function () { $rootScope.$apply(function () { deferred.resolve(); }); }); return deferred.promise; }] } }); $locationProvider.html5Mode(true); }]);
測試瀏覽器緩存
當開發一個 Web 應用程序時,一件你想要做的事情是:測試所有瀏覽器的緩存和緩存清除功能。你將會想要確保你的應用內容被正確下載並緩存,這些內容會在頁面請求之后出現。
你將會對你的內容做很多改變,來重建你的應用,以確保清除緩存和內容被再次下載時新版本號的問題能夠解決。
為了測試這一切,我在發布模式下通過 Chrome 瀏覽器來運行應用,並點擊 F12 來打開網絡標簽。在這里,你可以看見下載你的應用花費了多少時間和來自於服務器的內容,或者是瀏覽器的緩存。你甚至可以看到捆綁包的下載情況。
最終,你點擊你的應用程序的所有頁面,你會發現,所有的內容是從瀏覽器緩存來了,這是單頁應用的美麗之處。你的所有內容都會以獲取更大的緩存響應時間為結束,唯一要做的點擊 web 服務器來從呈現在頁面中的 RESTful Web API 來返回 JSON 格式的數據。
其它有趣的點
其它實例應用中有趣的點,還包括執行在服務器端的 .NET 庫。對於數據的有效性輸入,應用在業務處理中使用了 FluentValidation 庫。
FluentValidation 是 .NET 的一個使用流暢的界面和 lambda 表達式建立驗證規則的小型驗證庫。
當試圖創建示例應用程序的客戶時,客戶代碼和公司名稱為必填項。示例應用程序的業務層管理有效性,使用了 FluentValidation 庫驗證。通過將一個密集的客戶對象傳入到 CreateCustomer 方法中,對象上的屬性可以通過設置的 FluentValidation 表達式的業務規則被驗證。如果該業務對象驗證失敗,業務層可以從驗證庫返回錯誤的集合,並發送錯誤收集結果到客戶端,以便瀏覽器端錯誤信息的呈現。
public Customer CreateCustomer(Customer customer, out TransactionalInformation transaction) { transaction = new TransactionalInformation(); try { CustomerBusinessRules customerBusinessRules = new CustomerBusinessRules(); ValidationResult results = customerBusinessRules.Validate(customer); bool validationSucceeded = results.IsValid; IList<ValidationFailure> failures = results.Errors; if (validationSucceeded == false) { transaction = ValidationErrors.PopulateValidationErrors(failures); return customer; } _customerDataService.CreateSession(); _customerDataService.BeginTransaction(); _customerDataService.CreateCustomer(customer); _customerDataService.CommitTransaction(true); transaction.ReturnStatus = true; transaction.ReturnMessage.Add("Customer successfully created."); } catch (Exception ex) { string errorMessage = ex.Message; transaction.ReturnMessage.Add(errorMessage); transaction.ReturnStatus = false; } finally { _customerDataService.CloseSession(); } return customer; }
下面是定義了客戶對象的業務規則類,使用 FluentValidation 庫,定義一組 lambda 表達式並創建業務規則和每個驗證相關的錯誤信息。該 FluentValidation 庫使用了一組不同的 lambda 表達式來驗證業務對象或實體。
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; using FluentValidation; using CodeProject.Business.Entities; using System.Configuration; using CodeProject.Interfaces; namespace CodeProject.Business { public class CustomerBusinessRules : AbstractValidator<Customer> { public CustomerBusinessRules() { RuleFor(c => c.CompanyName).NotEmpty().WithMessage("Company Name is required."); RuleFor(c => c.CustomerCode).NotEmpty().WithMessage("Customer Code is required."); } } }
在示例應用程序中另一個值得注意的點,是使用 Ninject 庫的依賴注入的實現。當 Ninject從NuGet 安裝時,一個配置文件 NinjectWebCommon.cs 就會為你創建。在這里,你可以告訴 Ninject 庫當應用的某些部分被執行時,要創建哪些對象,比如在 Web API 服務中。在下面的 RegisterServices 中,我告訴 Ninject 分配客戶數據服務和產品數據服務到他們各自實現的接口中。這就告訴了 Ninject 去哪兒加載匹配的 dll 引用。
namespace CodeProject.Portal.App_Start { using System; using System.Web; using Microsoft.Web.Infrastructure.DynamicModuleHelper; using Ninject; using Ninject.Web.Common; public static class NinjectWebCommon { private static readonly Bootstrapper bootstrapper = new Bootstrapper(); /// <summary>
/// Starts the application /// </summary>
public static void Start() { DynamicModuleUtility.RegisterModule(typeof(OnePerRequestHttpModule)); DynamicModuleUtility.RegisterModule(typeof(NinjectHttpModule)); bootstrapper.Initialize(CreateKernel); } /// <summary>
/// Stops the application. /// </summary>
public static void Stop() { bootstrapper.ShutDown(); } /// <summary>
/// Creates the kernel that will manage your application. /// </summary>
/// <returns>The created kernel.</returns>
private static IKernel CreateKernel() { var kernel = new StandardKernel(); try { kernel.Bind<Func<IKernel>>().ToMethod(ctx => () => new Bootstrapper().Kernel); kernel.Bind<IHttpModule>().To<HttpApplicationInitializationHttpModule>(); RegisterServices(kernel); System.Web.Http.GlobalConfiguration.Configuration.DependencyResolver =
new Ninject.Web.WebApi.NinjectDependencyResolver(kernel); return kernel; } catch { kernel.Dispose(); throw; } } /// <summary>
/// Load your modules or register your services here! /// </summary>
/// <param name="kernel">The kernel.</param>
private static void RegisterServices(IKernel kernel) { kernel.Bind<CodeProject.Interfaces.ICustomerDataService>(). To<CodeProject.Data.EntityFramework.CustomerDataService>(); kernel.Bind<CodeProject.Interfaces.IProductDataService>(). To<CodeProject.Data.EntityFramework.ProductDataService>(); } } }
使用 Ninject 數據注解[注入],你可以告訴 Ninject 庫何時何地實例化你的對象。在下面的網頁 API 服務,客戶數據服務就是由 Ninject 創建的。由於客戶業務服務依賴於客戶數據的服務來訪問數據,客戶數據服務應該被注入客戶業務服務的構造函數中。所有這一切都是通過創建客戶數據的服務接口,然后簡單地實現了客戶數據服務接口來完成的。依賴注入是功能強大的,因為它創造應用代碼彼此分離的耦合度低的應用層。
using System; using System.Collections.Generic; using System.Linq; using System.Net; using System.Net.Http; using System.Web.Http; using CodeProject.Portal.Models; using CodeProject.Business.Entities; using CodeProject.Business; using CodeProject.Interfaces; using Ninject; namespace CodeProject.Portal.WebApiControllers { [RoutePrefix("api/CustomerService")] public class CustomerServiceController : ApiController { [Inject] public ICustomerDataService _customerDataService { get; set; } /// <summary>
/// Create Customer /// </summary>
/// <param name="request"></param>
/// <param name="customerViewModel"></param>
/// <returns></returns>
[Route("CreateCustomer")] [HttpPost] public HttpResponseMessage CreateCustomer(HttpRequestMessage request, [FromBody] CustomerViewModel customerViewModel) { TransactionalInformation transaction; Customer customer = new Customer(); customer.CompanyName = customerViewModel.CompanyName; customer.ContactName = customerViewModel.ContactName; customer.ContactTitle = customerViewModel.ContactTitle; customer.CustomerCode = customerViewModel.CustomerCode; customer.Address = customerViewModel.Address; customer.City = customerViewModel.City; customer.Region = customerViewModel.Region; customer.PostalCode = customerViewModel.PostalCode; customer.Country = customerViewModel.Country; customer.PhoneNumber = customerViewModel.PhoneNumber; customer.MobileNumber = customerViewModel.MobileNumber; CustomerBusinessService customerBusinessService =
new CustomerBusinessService(_customerDataService); customerBusinessService.CreateCustomer(customer, out transaction); if (transaction.ReturnStatus == false) { customerViewModel.ReturnStatus = false; customerViewModel.ReturnMessage = transaction.ReturnMessage; customerViewModel.ValidationErrors = transaction.ValidationErrors; var responseError = Request.CreateResponse<CustomerViewModel> (HttpStatusCode.BadRequest, customerViewModel); return responseError; } customerViewModel.CustomerID = customer.CustomerID; customerViewModel.ReturnStatus = true; customerViewModel.ReturnMessage = transaction.ReturnMessage; var response = Request.CreateResponse<CustomerViewModel> (HttpStatusCode.OK, customerViewModel); return response; }
結論
在 ASP.NET MVC 和 ASP.NET 捆綁中集成 AngularJS 似乎是一個開始時看起來像挑戰的嘗試。在試驗和失敗的每次迭代中,這個挑戰變得逐漸變得不那么難。我只是想使所有這些集成起來工作,我不會停止努力。
你可以爭論在 ASP.NET 中使用捆綁和縮功能和在 Grunt 與 Gulp 部分使用流行的壓縮工具,其各自的優點。如果你是一個無需學習另外技術和工具並且喜歡點擊按鈕來發布你的 Visual Studio 的微軟開發人員,你很可能會想使用 ASP.NET 捆綁功能。我發現這個功能確實是我想要的,它只是花費了我很長的時間來弄清楚如何將它與 AngularJS 集成。
在這些天里,有很多技術可以來寫。我以后的一些文章中可能包括 AngularJS 2 和 MEAN 的其余部分,包括 Node.js 的,Express 和 MongoDB。
還有一些包含在最新發布的 Visual Studio 2015 中的一些使用 Apache Cordov 開發的移動應用。這種先進的 HTML 混合的移動應用框架很可能可以和 Apache Cordov 一起工作使用。據說 Ionic 能夠使用 HTML 和 AngularJS ,並且可以很容易的建立大規模交互式的移動應用。敬請期待!
以上所有內容即為作者實現如何在 ASP.NET MVC 中集成 AngularJS 的具體思路以及詳細的解決方法。后續我們自己在進行 ASP.NET MVC 和 AngularJS 開始時,還可以借助開發工具來助力開發過程。ASP.NET MVC開發時,可以借助 ComponentOne Studio ASP.NET MVC 這一款輕量級控件,它與 Visual Studio 無縫集成,完全與 MVC6 和 ASP.NET 5.0 兼容,將大幅提高工作效率;AngularJS 開發時,可以借助 Wijmo 這款為企業應用程序開發而推出的一系列包含 HTML5 和 JavaScript 的開發控件集,無論應用程序是移動端、PC端、還是必須要支持IE6,Wijmo 均能滿足需求。
文章來源:By
原文鏈接:http://www.codeproject.com/Articles/1033076/Integrating-AngularJS-with-ASP-NET-MVC
相關閱讀: