今天来为大家介绍如何在 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[];
var controllerName = path[]; 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[];
var controllerName = path[]; 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 Mark J. Caplin

原文链接:http://www.codeproject.com/Articles/1033076/Integrating-AngularJS-with-ASP-NET-MVC

相关阅读:

是什么让C#成为最值得学习的编程语言

从Visual Studio看微软20年技术变迁

C#开发人员应该知道的13件事情

Visual Studio 2017正式版发布全纪录

如何在 ASP.NET MVC 中集成 AngularJS(3)的更多相关文章

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

    在如何在 ASP.NET MVC 中集成 AngularJS(1)中,我们介绍了 ASP.NET MVC 捆绑和压缩.应用程序版本自动刷新和工程构建等内容. 下面介绍如何在 ASP.NET MVC 中 ...

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

    介绍 当涉及到计算机软件的开发时,我想运用所有的最新技术.例如,前端使用最新的 JavaScript 技术,服务器端使用最新的基于 REST 的 Web API 服务.另外,还有最新的数据库技术.最新 ...

  3. 如何在asp.net mvc中添加自定义的HTML辅助种方法

    很久没在博客园发表文章了,今天来总结一下如何在asp.net mvc中添加自定义的HTML辅助方法.我们现在设计这么一个目前,利用自定义的HTML方法来渲染一个普通的img标记.直接进入主题吧: 首先 ...

  4. 如何在ASP.NET MVC 中获取当前URL、controller、action

    一.URL的获取很简单,ASP.NET通用: [1]获取 完整url (协议名+域名+虚拟目录名+文件名+参数) string url=Request.Url.ToString(); [2]获取 虚拟 ...

  5. 如何在asp.net mvc 中使用Autofac 控制反转(Ioc)

    前言 最近看了一些关于Ioc方面的开源项目,里面的类跳来转去,看的迷迷糊糊的.项目里根本不需要这么“复杂的”设计,只需简单完成Ico,达到解耦的目的,并且能高效的完成项目.于是参考autofac的官网 ...

  6. MVC 中集成 AngularJS1

    在 ASP.NET MVC 中集成 AngularJS(1)   介绍 当涉及到计算机软件的开发时,我想运用所有的最新技术.例如,前端使用最新的 JavaScript 技术,服务器端使用最新的基于 R ...

  7. angular.js的路由和模板在asp.net mvc 中的使用

    angular.js的路由和模板在asp.net mvc 中的使用 我们知道angular.js是基于mvc 的一款优秀js框架,它也有一套自己的路由机制,和asp.net mvc 路由不太一样.as ...

  8. 如何简单的在 ASP.NET Core 中集成 JWT 认证?

    前情提要:ASP.NET Core 使用 JWT 搭建分布式无状态身份验证系统 文章超长预警(1万字以上),不想看全部实现过程的同学可以直接跳转到末尾查看成果或者一键安装相关的 nuget 包 自上一 ...

  9. 《Entity Framework 6 Recipes》中文翻译系列 (20) -----第四章 ASP.NET MVC中使用实体框架之在MVC中构建一个CRUD示例

    翻译的初衷以及为什么选择<Entity Framework 6 Recipes>来学习,请看本系列开篇 第四章  ASP.NET MVC中使用实体框架 ASP.NET是一个免费的Web框架 ...

随机推荐

  1. 学习CodeIgniter框架之旅(一)自定义模板目录

    在常用的框架本身都已经做好了分层和目录结构,但这在很多时候不满足项目的需求甚至在某些情况下变得不合理,因此很多时候需要自定义目录结构,在此就看看如果在CodeIgniter框架中自定义模板目录: 在C ...

  2. Java中将0x开头的十六进制字符串转换成十进制整数

    1.Integer.toString(int i) 由于input(输入数据)是以0x开头的字符串,并不是整型.因而在用 String s = Integer.toString(input); 时用会 ...

  3. c语言的输入输出函数

    参考文章: http://blog.sina.com.cn/s/blog_784f40b80100psg9.html C语言输入输出函数分为两类: 1.格式化输入输出函数 2.非格式化输入输出 --- ...

  4. 从range和xrange的性能对比到yield关键字(中)

    上节提出了range和xrange的效率问题,这节我们来探究其中的原因   yield的使用   我们看下面的程序: #coding: utf-8 def test(): print 4 print ...

  5. 每天一个linux命令(15):whereis 命令

    whereis命令只能用于程序名的搜索,而且只搜索二进制文件(参数-b).man说明文件(参数-m)和源代码文件(参数-s).如果省略参数,则返回所有信息. 和 find相比,whereis查找的速度 ...

  6. 如何使用Instruments诊断App(Swift版):起步-b

    无论你写过许多iOS应用,还是刚刚开始你的第一个应用,毫无疑问,你都会想出一些新点子,或者想去弄明白你该怎么做,来让你的app变得更好. 除去添加新特性来优化你的应用,有一件事是所有好的开发者都回去做 ...

  7. ecshop标签大全 各个页面常用标签大全

    先从index.php主页开始 页面关键字 {$keywords } 页面标题 {$page_title} 产品分类 父分类列表 {foreach from=$categories item=cat ...

  8. 【七】注入框架RoboGuice使用:(Your First Custom Binding)

    上一篇我们简单的介绍了一下RoboGuice的使用([六]注入框架RoboGuice使用:(Singletons And ContextSingletons)),今天我们来看下自己定义绑定(bindi ...

  9. 亲测可用的国内maven镜像

    maven作为一个项目管理工具确实非常好用,之前oschina的中央仓库可用,现在oschina的maven服务器关了,于是自己倒腾了一个nexus,苦于自己的服务器是入门级的,下载速度实在让人着急. ...

  10. ASP.NET Core 项目简单实现身份验证及鉴权

    ASP.NET Core 身份验证及鉴权 目录 项目准备 身份验证 定义基本类型和接口 编写验证处理器 实现用户身份验证 权限鉴定 思路 编写过滤器类及相关接口 实现属性注入 实现用户权限鉴定 测试 ...