专栏首页葡萄城控件技术团队如何在 ASP.NET MVC 中集成 AngularJS(3)

如何在 ASP.NET MVC 中集成 AngularJS(3)

今天来为大家介绍如何在 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 均能满足需求。

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

我来说两句

0 条评论
登录 后参与评论

相关文章

  • “别更新了,学不动了” 之:全栈开发者 2019 应该学些什么?

    对于什么是全栈开发者并没有一个明确的定义。但是,有一件事是肯定的:2019 年对全栈开发者的需求量很大。在本文中,我将向你概述一些趋势,你可以尝试根据这些趋势来...

    葡萄城控件
  • ActiveReports 报表应用教程 (9)---交互式报表之动态排序

    在葡萄城ActiveReports报表中除了提供对数据源进行排序的功能之外,还提供了最终用户排序功能,最终用户可以对报表进行区域内排序和整个数据源排序,结合数据...

    葡萄城控件
  • Spread for Windows Forms快速入门(1)---开始使用Spread

    前言 Spread for Windows Forms是功能最为强大的表格控件,拥有灵活开放的对象模型和50,000个以上的API,使得开发人员几乎可以定制所有...

    葡萄城控件
  • PHP连接mysql

    昨天介绍了一下mysql的简单操作,今天来说一下mysql如何和php连接在一起!

    十月梦想
  • 决胜未来,2019年前端开发十大战略性技术布局

    2010年的你,如果能学会Android开发,现在的你,薪资不会低于年薪50万……

    前端大彬哥
  • Telnet 配置实验

    3、R1 上创建用户“wangdaye”,密码“123456”用于 Telnet 身份验证

    网络技术联盟站
  • Springboot打包支持第三方jar

    springboot在打包的时候,调用spring-boot-maven-plugin,执行repackage把tomcat和resource,lib等合成一个...

    Ryan-Miao
  • maven中如何将所有引用的jar包打包到一个jar中

    wuweixiang
  • 消费者驱动的微服务契约测试套件:Spring Cloud Contract

    在微服务架构下,你的服务可能由不同的团队提供和维护,在这种情况下,接口的开发和维护可能会带来一些问题,比如服务端调整架构或接口调整而对消费者不透明,导致接口调用...

    程序猿DD
  • 关于win10系统安装VMware12Pro后,win10系统的 控制面板\网络和 Internet\网络连接\更改适配器选项卡中 没有虚拟网卡VMnet1和VMnet8图标,该如何把他们显示出来呢?

    安装VMware12Pro后,PC主机通过命令行:ipconfig/all ,查看发现没有 VMnet1 和VMnet8 。

    黑泽君

扫码关注云+社区

领取腾讯云代金券