65.9K
CodeProject 正在变化。 阅读更多。
Home

AngularJS 和 ASP.NET MVC 入门 - 第一部分

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.93/5 (260投票s)

2014 年 8 月 10 日

CPOL

11分钟阅读

viewsIcon

1528492

如何在 ASP.NET MVC 上快速上手 AngularJS:第一部分

欢迎来到关于 AngularJS 和 ASP.NET MVC 系列文章的第一部分,以及如何充分利用这两者。

系列文章

引言

客户端 MV* 框架目前非常流行,我最喜欢的无疑是 AngularJS。本文将向您展示如何在使用我最喜欢的服务器端 MVC 框架 ASP.NET MVC (5) 的同时,快速上手 AngularJS。

源代码

本文附带的源代码可以在 这里 找到。

你需要什么

我使用的是 Visual Studio 2013 Update 3,因此我只能保证以下内容在此 IDE 中按预期工作,但非官方地,只要您使用的是 MVC 4 或更高版本,您应该可以在任何 VS 版本 >= 2010 上进行跟随(至少对于我们要做的内容来说,事情还没有发生如此剧烈的变化)。Express 版本也应该没问题。

让我们开始吧

好的,首先,您需要创建一个新的 MVC 项目,在开始屏幕上选择 **新建项目...**,然后依次导航到 *Templates* => *Visual C#*(或者 *Visual Basic*,如果您喜欢,并且有信心将本文中的 C# 代码翻译成 VB 等效代码)=> *Web* => *ASP.NET Web Application* => *MVC*。将 **Authentication** 保留为 Individual User Accounts,然后单击确定。

现在我们有了默认的 MVC 模板,其中包含了很多我们需要的,以及一些我们不需要的东西,所以让我们摆脱那些。从“视图”=>“其他窗口”=>“程序包管理器控制台”打开 **程序包管理器控制台**,如下图所示:

大屠杀

运行以下命令

  • Uninstall-Package Microsoft.jQuery.Unobtrusive.Validation
  • Uninstall-Package jQuery.Validation
  • Uninstall-Package jQuery
  • Uninstall-Package Modernizr
  • Uninstall-Package Respond
  • Uninstall-Package bootstrap

现在我们需要更新我们的 BundleConfig.cs 类以反映这一点,我的现在看起来像这样:

using System.Web.Optimization;

namespace AwesomeAngularMVCApp
{
    public class BundleConfig
    {
        public static void RegisterBundles(BundleCollection bundles)
        {
            bundles.Add(new StyleBundle("~/Content/css").Include(
                      "~/Content/site.css"));

            BundleTable.EnableOptimizations = true;
        }
    }
}

从 **Controllers** 目录中删除以下文件:

  • AccountController.cs
  • ManageController.cs

以及从 **Views** 目录中删除以下目录:

  • Account
  • Manage
  • 共享

顺便说一句,也请从 **Views** 目录中删除 **_ViewStart.cshtml**,然后从 **Views/Home** 目录中删除以下内容:

  • About.cshtml
  • Contact.cshtml

接下来,从 **Controllers/HomeController.cs** 中删除以下 Action 方法:

  • 关于
  • 联系方式

最后,打开 **Views** => **Home** => **Index.cshtml**,并删除其中找到的所有内容(不留活口),对 **Content** => **Site.css** 也做同样的处理(删除内容,保留文件)。

似乎我们只是删除了所有东西,为什么我们不使用空白模板呢?

Visual Studio 在我们选择此模板时添加了我们需要的用于身份验证等的所有正确库/样板代码。删除不必要的 HTML/JavaScript 比使用空白模板然后添加身份验证所需的所有内容要快得多。

你好 AngularJS

将以下内容添加到 **Views** => **Home** 中现在为空的 Index.cshtml 文件中:

<!DOCTYPE html>
<html ng-app>
<head>
    <title ng-bind="helloAngular"></title>
</head>
<body>
    
    <input type="text" ng-model="helloAngular" />
    <h1>{{helloAngular}}</h1>
    
    <script src="//cdnjs.cloudflare.com/ajax/libs/angular.js/1.2.20/angular.min.js"></script>
</body>
</html>

很酷,现在按 F5(或者您在 Visual Studio 中配置的调试键),然后在文本框中输入一些内容。一切顺利的话,您应该会看到类似这样的内容:

如果您看到更接近 **{{helloAngular}}** 的内容,那么您在某个地方犯了错误。在这里,您的首要任务应该是浏览器的开发者控制台。在 Chrome 中,按 **F12** 然后在出现的窗口中选择 Console 选项卡即可找到它。在下面的示例中,我的 Angular 应用无法正常工作,因为我错误地输入了 angular.js 文件的 URL(哎呀!)。

这里发生了什么?

我在此假定一切都按预期工作。您在 index 文件中输入的 HTML 是一个完整的 Angular 应用程序!

我们所做的只是加载 Angular 运行时本身,声明一个模型对象(Angular 中的模型类似于 .Net 中模型的属性),并且该模型在 3 个地方进行了绑定:页面的标题、**h1** 标签的内容以及文本输入控件的 **value** 属性。

编辑文本框会导致绑定到模型的其他两个地方自动更新。这就是所谓的双向绑定,是不是很酷?

这不是一个好习惯

以上是一种快速演示非常基础的 AngularJS 应用程序的绝佳方式,但这不是您在现实世界中编写 Angular 应用的方式。

我们需要添加一些样板代码,我们现在就来做。

  • 在 **Scripts** 中添加一个名为 **Controllers** 的目录。
  • 在 **Scripts** 的根目录下添加一个名为您的应用程序名称的 JavaScript 文件(我的名字是 **AwesomeAngularMVCApp.js**)。
  • 在 **Scripts** => **Controllers** 目录中添加一个名为 **LandingPageController.js** 的 JavaScript 文件。
  • 现在让我们为此添加一个 Bundle 到 BundleConfig.cs,类似这样:
bundles.Add(new ScriptBundle("~/bundles/AwesomeAngularMVCApp")
    .IncludeDirectory("~/Scripts/Controllers", "*.js")
    .Include("~/Scripts/AwesomeAngularMVCApp.js"));

并将此 Bundle 添加到主页,在 angular.min 脚本标记之后:

<script src="//cdnjs.cloudflare.com/ajax/libs/angular.js/1.2.20/angular.min.js"></script>
@Scripts.Render("~/bundles/AwesomeAngularMVCApp")
  • 接下来,我们需要编写我们的 LandingPageController,它应该看起来像这样:
var LandingPageController = function($scope) {
    $scope.models = {
        helloAngular: 'I work!'
    };
}

// The $inject property of every controller (and pretty much every other type of object in Angular) needs to be a string array equal to the controllers arguments, only as strings
LandingPageController.$inject = ['$scope'];
  • 之后,我们想要设置我们自己的应用程序对象,并告诉它关于控制器的事情。将以下内容添加到 **AwesomeAngularMVCApp.js**(或您的文件名称):
var AwesomeAngularMVCApp = angular.module('AwesomeAngularMVCApp', []);

AwesomeAngularMVCApp.controller('LandingPageController', LandingPageController);

做得好,现在我们想更新我们的主页视图,使其能够正确绑定到我们的 angular 应用程序和 Landing Page 控制器。

<!DOCTYPE html>
<html ng-app="AwesomeAngularMVCApp" ng-controller="LandingPageController">
<head>
    <title ng-bind="models.helloAngular"></title>
</head>
<body>
    <input type="text" ng-model="models.helloAngular" />
    <h1>{{models.helloAngular}}</h1>

    <script src="//cdnjs.cloudflare.com/ajax/libs/angular.js/1.2.20/angular.min.js"></script>
    @Scripts.Render("~/bundles/AwesomeAngularMVCApp")
</body>
</html>

一切顺利的话,下次调试时您应该会看到这个:

路由

Angular 等客户端 MV* 框架在路由方面非常强大。这为我们提供了构建单页应用程序的基础。

它的工作方式相当直接。我们在主页上放置一个 div,然后告诉 angular,每当 URL 匹配特定模式时,就将另一个 HTML 文件的内容放入该 div 中。

让我们为我们的应用程序添加一些路由:

  • 首先,我们需要包含 Angular 的 ngRoute 模块,我将从 Cloudflare 下载它。
  • 接下来,让我们将容器 div 添加到我们的主页中,放在 body 标签内,放在脚本标签之前。
  • 最后,我们将在主页中添加几个链接,当点击这些链接时,它们将更新路由并将相应的视图加载到容器 div 中。

我的 HTML 目前看起来像这样:

<!DOCTYPE html>
<html ng-app="AwesomeAngularMVCApp" ng-controller="LandingPageController">
<head>
    <title ng-bind="models.helloAngular"></title>
</head>
<body>
    <h1>{{models.helloAngular}}</h1>

    <ul>
        <li><a href="/#/routeOne">Route One</a></li>
        <li><a href="/#/routeTwo">Route Two</a></li>
        <li><a href="/#/routeThree">Route Three</a></li>
    </ul>

    <div ng-view></div>

    <script src="//cdnjs.cloudflare.com/ajax/libs/angular.js/1.2.20/angular.min.js"></script>
    <script src="//cdnjs.cloudflare.com/ajax/libs/angular.js/1.2.20/angular-route.min.js"></script>
    @Scripts.Render("~/bundles/AwesomeAngularMVCApp")
    </body>
</html>

现在,我们需要告诉我们的 AngularJS 应用程序关于这些路由的信息。我们在应用程序模块的 config 函数中完成此操作(在声明应用程序的同一个 JavaScript 文件中,位于我们的 Scripts 目录的根目录下),我的现在看起来像这样:

var AwesomeAngularMVCApp = angular.module('AwesomeAngularMVCApp', ['ngRoute']);

AwesomeAngularMVCApp.controller('LandingPageController', LandingPageController);

var configFunction = function ($routeProvider) {
    $routeProvider.
        when('/routeOne', {
            templateUrl: 'routesDemo/one'
        })
        .when('/routeTwo', {
            templateUrl: 'routesDemo/two'
        })
        .when('/routeThree', {
            templateUrl: 'routesDemo/three'
        });
}
configFunction.$inject = ['$routeProvider'];

AwesomeAngularMVCApp.config(configFunction);

到目前为止一切顺利,但这些视图尚不存在。让我们解决这个问题,在 MVC 控制器目录中创建一个名为 **RoutesDemoController** 的 C# 控制器,并添加三个名为 **One**、**Two** 和 **Three** 的 Action 方法,如下所示:

using System.Web.Mvc;

namespace AwesomeAngularMVCApp.Controllers
{
    public class RoutesDemoController : Controller
    {
        public ActionResult One()
        {
            return View();
        }

        public ActionResult Two()
        {
            return View();
        }

        public ActionResult Three()
        {
            return View();
        }
    }
}

右键单击 **return View();** 的每个实例,然后选择 **添加视图...**,在以下对话框中勾选 **创建为部分视图**,保留其他所有内容不变,然后单击 **添加**:

为每个视图添加一些内容,以便您可以唯一地识别它,我在每个视图中都添加了“Route One”、“Route Two”和“Route Three”等字样。

重要提示:此时,您可能会发现 Visual Studio 好心地 重新添加了一些我们之前删除的内容。如果这样,您可能需要再次浏览“入门”部分,以确保没有不应该存在的东西。抱歉!

现在按 F5。如果一切顺利,您应该能够点击每个链接,将该视图的内容加载到容器 div 中,如下所示:

刷新页面也有效,后退按钮也有效。我们实际上并没有离开主页,但我们可以动态地将内容加载到 div 中,后退以转到上一个内容,当刷新时,页面的状态与之前相同。

'Route two' 接受参数

让我们修改 route two 以接受一个参数,该参数将从 angular 通过我们的 MVC 控制器传递到我们的 MVC 视图。更新 C# Action 方法如下:

public ActionResult Two(int donuts = 1)
{
    ViewBag.Donuts = donuts;
    return View();
}

现在更新视图本身,使其看起来像这样:

Route two

@for (var i = 0; i < ViewBag.Donuts; i++)
{
    <p>Mmm, donuts...</p>
}

现在我们需要告诉 angular 关于这个参数。在 AwesomeAngularMVCApp.js 中修改 config 函数如下:

var configFunction = function ($routeProvider) {
    $routeProvider.
        when('/routeOne', {
            templateUrl: 'routesDemo/one'
        })
        .when('/routeTwo/:donuts', {
            templateUrl: function (params) { return '/routesDemo/two?donuts=' + params.donuts; }
        })
        .when('/routeThree', {
            templateUrl: 'routesDemo/three'
        });
}

主页上的 route two 超链接可以看起来像这样:

<li><a href="/#/routeTwo/6">Route Two</a></li>

'Route three' 仅限注册用户

按照目前的情况,互联网上的任何人都可以访问我们的网站并查看路由一、二和三的内容。这很棒,但路由三将包含一些敏感数据,我们如何保护它?

当然是使用 ASP.NET 身份验证。

  • 将 Authorize 属性添加到 Route three 的 Action 方法,如下所示:
[Authorize]
public ActionResult Three()
{
    return View();
}

如果我们此时运行我们的应用程序并尝试导航到路由三,我们将在浏览器的开发者控制台中看到这种史诗般的(错误)信息:

这实际上意味着到目前为止一切都按预期工作,但在我们继续处理路由三之前,让我们先进行一些身份验证。

身份验证

目前,ASP.NET 检测到用户无权访问“Route 3”并尝试重定向到“/Account/Login”,将我们从主页重定向。我们真正想要的是让 Angular 自己处理这个 401 响应。您可以使用拦截器来实现这一点。

我们现在就来做。第一步是阻止 ASP.NET MVC 在 401 时重定向,编辑 **App_Start** => **Startup.Auth.cs**。找到以下内容:

LoginPath = new PathString("/Account/Login")

并替换为:

LoginPath = new PathString(string.Empty)

现在,当我们尝试访问“Route 3”时,我们在浏览器开发者控制台中收到以下错误:

好多了。让我们使用拦截器来处理这个问题。在 **Scripts** 中创建一个名为 **Factories** 的目录,并更新 **BundleConfig** 中的 ScriptBundle 以包含此目录。

 bundles.Add(new ScriptBundle("~/bundles/AwesomeAngularMVCApp")
    .IncludeDirectory("~/Scripts/Controllers", "*.js")
    .IncludeDirectory("~/Scripts/Factories", "*.js")
    .Include("~/Scripts/AwesomeAngularMVCApp.js"));

在 **Scripts** => **Factories** 中创建一个新的 JavaScript 文件,名为 **AuthHttpResponseInterceptor.js**,其中包含以下 Angular 工厂(借用自 SparkTree 博客):

var AuthHttpResponseInterceptor = function($q, $location) {
    return {
        response: function (response) {
            if (response.status === 401) {
                console.log("Response 401");
            }
            return response || $q.when(response);
        },
        responseError: function (rejection) {
            if (rejection.status === 401) {
                console.log("Response Error 401", rejection);
                $location.path('/login').search('returnUrl', $location.path());
            }
            return $q.reject(rejection);
        }
    }
}

AuthHttpResponseInterceptor.$inject = ['$q', '$location'];

现在我们需要让我们的 Angular 应用知道拦截器,并配置它来使用它。更新 **AwesomeAngularMVCApp.js** 如下:

var AwesomeAngularMVCApp = angular.module('AwesomeAngularMVCApp', ['ngRoute']);

AwesomeAngularMVCApp.controller('LandingPageController', LandingPageController);
AwesomeAngularMVCApp.controller('LoginController', LoginController);

AwesomeAngularMVCApp.factory('AuthHttpResponseInterceptor', AuthHttpResponseInterceptor);

var configFunction = function ($routeProvider, $httpProvider) {
    $routeProvider.
        when('/routeOne', {
            templateUrl: 'routesDemo/one'
        })
        .when('/routeTwo/:donuts', {
            templateUrl: function (params) { return '/routesDemo/two?donuts=' + params.donuts; }
        })
        .when('/routeThree', {
            templateUrl: 'routesDemo/three'
        })
        .when('/login', {
            templateUrl: '/Account/Login',
            controller: LoginController
        });

    $httpProvider.interceptors.push('AuthHttpResponseInterceptor');
}
configFunction.$inject = ['$routeProvider', '$httpProvider'];

AwesomeAngularMVCApp.config(configFunction);

现在我们已经实现了 Angular 在身份验证失败时正确重定向。我们只需要有一个重定向目标。

在本教程的开头,我要求您删除 C# AccountController,现在我又要请您创建一个 C# AccountController。这可能显得适得其反,但请跟我来。您删除的文件近 500 行,而我们只需要其中的一小部分。此外,了解每行代码的作用,而不是将 ASP.NET 中的身份验证视为黑盒子,这很有好处。

因此,请继续添加一个 C# Account 控制器,最初包含以下内容:

using System.Web.Mvc;

namespace AwesomeAngularMVCApp.Controllers
{
    [Authorize]
    public class AccountController : Controller
    {
        [AllowAnonymous]
        public ActionResult Login()
        {
            return View();
        }
    }
}

使用 Visual Studio 以您之前的方式创建 Login 视图。此视图将需要一个 Angular 控制器,所以让我们在 **Scripts** => **Controllers** 中添加一个名为 **LoginController.js** 的 JavaScript 文件,其中包含以下 JavaScript:

var LoginController = function($scope, $routeParams) {
    $scope.loginForm = {
        emailAddress: '',
        password: '',
        rememberMe: false,
        returnUrl: $routeParams.returnUrl
    };

    $scope.login = function() {
        //todo
    }
}

LoginController.$inject = ['$scope', '$routeParams'];

**AwesomeAngularMVCApp.js** 中的路由需要更新,以便 Angular 知道为登录视图提供上述控制器。我们还需要告诉我们的应用程序模块该控制器已存在。

var AwesomeAngularMVCApp = angular.module('AwesomeAngularMVCApp', ['ngRoute']);

AwesomeAngularMVCApp.controller('LandingPageController', LandingPageController);
AwesomeAngularMVCApp.controller('LoginController', LoginController);

var configFunction = function($routeProvider, $routeParams) {
    $routeProvider.
        when('/routeOne', {
            templateUrl: 'routesDemo/one'
        })
        .when('/routeTwo/:donuts', {
            templateUrl: function(params) { return '/routesDemo/two?donuts=' + params.donuts; }
        })
        .when('/routeThree', {
            templateUrl: 'routesDemo/three'
        })
        .when('/login?returnUrl', {
            templateUrl: 'Account/Login',
            controller: LoginController
        });
}
configFunction.$inject = ['$routeProvider'];

AwesomeAngularMVCApp.config(configFunction);

现在让我们将登录表单本身添加到 **Views** => **Account** => **Login.cshtml** 视图中:

<form ng-submit="login()">
    <label for="emailAddress">Email Address:</label>
    <input type="text" ng-model="loginForm.emailAddress" id="emailAddress" required />
    
    <label for="password">Password:</label>
    <input type="password" id="password" ng-model="loginForm.password" required />
    
    <label for="rememberMe">Remember Me:</label>
    <input type="checkbox" id="rememberMe" ng-model="loginForm.rememberMe" required />
    
    <button type="submit">Login</button>
</form>

现在,当我们尝试访问“Route three”时,将发生两件事:

  1. 我们收到一个 401 未授权响应,该响应由 Angular 在浏览器控制台中记录。
  2. 我们的拦截器将开始工作,并将我们重定向到我们创建的 Login 视图。

我们快完成了!我们还需要一个注册表单供新用户使用,让我们现在就创建它。

将以下 Action 方法添加到 **AccountController.cs**。

[AllowAnonymous]
public ActionResult Register()
{
    return View();
}

我们需要为 Register 视图创建一个 Angular 控制器。在 **Scripts** => **Controllers** 中添加一个名为 **RegisterController.js** 的 JavaScript 文件,其中包含以下代码:

var RegisterController = function($scope) {
    $scope.registerForm = {
        emailAddress: '',
        password: '',
        confirmPassword: ''
    };

    $scope.register = function() {
        //todo
    }
}

RegisterController.$inject = ['$scope'];

使用 Visual Studio 创建视图,其中包含以下 HTML(希望您开始识别模式):

<form ng-submit="register()">
    <label for="emailAddress">Email Address:</label>
    <input id="emailAddress" type="text" ng-model="registerForm.emailAddress" required />
    
    <label for="password">Password:</label>
    <input id="password" type="password" ng-model="registerForm.password" required />
    
    <label for="confirmPassword">Confirm Password:</label>
    <input id="confirmPassword" type="password" ng-model="registerForm.confirmPassword" required />
    
    <button type="submit">Register</button>
</form>

现在只需使用有关此新 angular 控制器和路由的信息更新 **AwesomeAngularMVCApp.js**:

var AwesomeAngularMVCApp = angular.module('AwesomeAngularMVCApp', ['ngRoute']);

AwesomeAngularMVCApp.controller('LandingPageController', LandingPageController);
AwesomeAngularMVCApp.controller('LoginController', LoginController);
AwesomeAngularMVCApp.controller('RegisterController', RegisterController);

AwesomeAngularMVCApp.factory('AuthHttpResponseInterceptor', AuthHttpResponseInterceptor);

var configFunction = function ($routeProvider, $httpProvider) {
    $routeProvider.
        when('/routeOne', {
            templateUrl: 'routesDemo/one'
        })
        .when('/routeTwo/:donuts', {
            templateUrl: function (params) { return '/routesDemo/two?donuts=' + params.donuts; }
        })
        .when('/routeThree', {
            templateUrl: 'routesDemo/three'
        })
        .when('/login', {
            templateUrl: '/Account/Login',
            controller: LoginController
        })
        .when('/register', {
            templateUrl: '/Account/Register',
            controller: RegisterController
        });

    $httpProvider.interceptors.push('AuthHttpResponseInterceptor');
}
configFunction.$inject = ['$routeProvider', '$httpProvider'];

AwesomeAngularMVCApp.config(configFunction);

现在是时候在我们的主页中添加登录和注册链接了:

<!DOCTYPE html>
<html ng-app="AwesomeAngularMVCApp" ng-controller="LandingPageController">
<head>
    <title ng-bind="models.helloAngular"></title>
</head>
<body>
    <h1>{{models.helloAngular}}</h1>

    <ul>
        <li><a href="/#/routeOne">Route One</a></li>
        <li><a href="/#/routeTwo/6">Route Two</a></li>
        <li><a href="/#/routeThree">Route Three</a></li>
    </ul>
    
    <ul>
        <li><a href="/#/login">Login</a></li>
        <li><a href="/#/register">Register</a></li>
    </ul>

    <div ng-view></div>

    <script src="//cdnjs.cloudflare.com/ajax/libs/angular.js/1.2.20/angular.min.js"></script>
    <script src="//cdnjs.cloudflare.com/ajax/libs/angular.js/1.2.20/angular-route.min.js"></script>
    @Scripts.Render("~/bundles/AwesomeAngularMVCApp")
    </body>
</html>

我们的 C# **AccountController** 需要一些工作。它缺少一些依赖项,因此我们将更新它以包含 **ApplicationUserManager** 和 **ApplicationSignInManager**,它们通过构造函数注入提供,并在出现回退情况时即时创建。我们还需要添加登录和注册方法。

using Microsoft.AspNet.Identity.Owin;
using System.Threading.Tasks;
using System.Web;
using System.Web.Mvc;
using AwesomeAngularMVCApp.Models;

namespace AwesomeAngularMVCApp.Controllers
{
    [Authorize]
    public class AccountController : Controller
    {
        private ApplicationUserManager _userManager;
        private ApplicationSignInManager _signInManager;

        public AccountController() { }

        public AccountController(ApplicationUserManager userManager, ApplicationSignInManager signInManager)
        {
            UserManager = userManager;
            SignInManager = signInManager;
        }

        public ApplicationUserManager UserManager
        {
            get { return _userManager ?? HttpContext.GetOwinContext().GetUserManager<ApplicationUserManager>(); }
            private set { _userManager = value; }
        }

        public ApplicationSignInManager SignInManager
        {
            get { return _signInManager ?? HttpContext.GetOwinContext().Get<ApplicationSignInManager>(); }
            private set { _signInManager = value; }
        }

        [AllowAnonymous]
        public ActionResult Login()
        {
            return View();
        }

        [AllowAnonymous]
        public ActionResult Register()
        {
            return View();
        }

        [HttpPost]
        [AllowAnonymous]
        public async Task<bool> Login(LoginViewModel model)
        {
            var result = await SignInManager.PasswordSignInAsync(model.Email, model.Password, model.RememberMe, false);
            switch (result)
            {
                case SignInStatus.Success:
                    return true;
                default:
                    ModelState.AddModelError("", "Invalid login attempt.");
                    return false;
            }
        }

        [HttpPost]
        [AllowAnonymous]
        public async Task<bool> Register(RegisterViewModel model)
        {
            var user = new ApplicationUser { UserName = model.Email, Email = model.Email };
            var result = await UserManager.CreateAsync(user, model.Password);
            if (!result.Succeeded) return false;
            await SignInManager.SignInAsync(user, false, false);
            return true;
        }
    }
}

我们非常接近了。下一步是填充相应 AngularJS 控制器中的 **login** 和 **register** 函数。然而,我们不希望将此逻辑放在控制器本身中,更好的方法是为每个函数创建一个 Angular 工厂,遵循单一职责原则和最少知识原则。

向 **Scripts** => **Factories** 添加一个名为 **LoginFactory.js** 的 Login 工厂。向工厂添加以下 JavaScript:

var LoginFactory = function ($http, $q) {
    return function (emailAddress, password, rememberMe) {

        var deferredObject = $q.defer();

        $http.post(
            '/Account/Login', {
                Email: emailAddress,
                Password: password,
                RememberMe: rememberMe
            }
        ).
        success(function (data) {
            if (data == "True") {
                deferredObject.resolve({ success: true });
            } else {
                deferredObject.resolve({ success: false });
            }
        }).
        error(function () {
            deferredObject.resolve({ success: false });
        });

        return deferredObject.promise;
    }
}

LoginFactory.$inject = ['$http', '$q'];

现在让我们告诉我们的 Angular 应用程序有关此工厂的信息:

AwesomeAngularMVCApp.factory('LoginFactory', LoginFactory);

将此工厂注入到我们的 LoginController 中,并在控制器调用 **login** 函数时调用它:

var LoginController = function ($scope, $routeParams, $location, LoginFactory) {
    $scope.loginForm = {
        emailAddress: '',
        password: '',
        rememberMe: false,
        returnUrl: $routeParams.returnUrl,
        loginFailure: false
    };

    $scope.login = function () {
        var result = LoginFactory($scope.loginForm.emailAddress, $scope.loginForm.password, $scope.loginForm.rememberMe);
        result.then(function(result) {
            if (result.success) {
                if ($scope.loginForm.returnUrl !== undefined) {
                    $location.path('/routeOne');
                } else {
                    $location.path($scope.loginForm.returnUrl);
                }
            } else {
                $scope.loginForm.loginFailure = true;
            }
        });
    }
}

LoginController.$inject = ['$scope', '$routeParams', '$location', 'LoginFactory'];

我们还需要对 Login 视图进行一些小的更新:

<form ng-submit="login()">
    <label for="emailAddress">Email Address:</label>
    <input type="email" ng-model="loginForm.emailAddress" id="emailAddress"/>
    
    <label for="password">Password:</label>
    <input type="password" id="password" ng-model="loginForm.password"/>
    
    <label for="rememberMe">Remember Me:</label>
    <input type="checkbox" id="rememberMe" ng-model="loginForm.rememberMe" />
    
    <button type="submit">Login</button>
</form>

<div ng-if="loginForm.loginFailure">
    D'oh!
</div>

如果一切顺利,尝试登录应该会产生以下结果:

那是因为我们还没有用户。让我们创建我们的 Registration 工厂。将 **RegistrationFactory.js** 添加到 **Scripts** => **Factories**,并添加以下 JavaScript:

var RegistrationFactory = function ($http, $q) {
    return function (emailAddress, password, confirmPassword) {

        var deferredObject = $q.defer();

        $http.post(
            '/Account/Register', {
                Email: emailAddress,
                Password: password,
                ConfirmPassword: confirmPassword
            }
        ).
        success(function (data) {
            if (data == "True") {
                deferredObject.resolve({ success: true });
            } else {
                deferredObject.resolve({ success: false });
            }
        }).
        error(function () {
            deferredObject.resolve({ success: false });
        });

        return deferredObject.promise;
    }
}

RegistrationFactory.$inject = ['$http', '$q'];

现在告诉 Angular 有关新工厂的信息:

AwesomeAngularMVCApp.factory('RegistrationFactory', RegistrationFactory);

然后将其注入到 RegisterController 中,并调用它的函数:

var RegisterController = function ($scope, $location, RegistrationFactory) {
    $scope.registerForm = {
        emailAddress: '',
        password: '',
        confirmPassword: '',
        registrationFailure: false
    };

    $scope.register = function () {
        var result = RegistrationFactory($scope.registerForm.emailAddress, $scope.registerForm.password, $scope.registerForm.confirmPassword);
        result.then(function (result) {
            if (result.success) {
                $location.path('/routeOne');
            } else {
                $scope.registerForm.registrationFailure = true;
            }
        });
    }
}

RegisterController.$inject = ['$scope', '$location', 'RegistrationFactory'];

现在,我们应该能够运行我们的应用程序,注册一个用户,并查看 'Route Three'。

第一部分到此结束

我试图在一篇文章中涵盖相当广泛的主题,并将在未来的文章中深入探讨所有这些领域,包括:

  • 重构第一部分中编写的代码
  • 防伪令牌
  • 指令
  • 服务
  • 提供者
  • Angular UI Router - 比 ng-route 更高级的 Angular 路由
  • Angular UI Bootstrap 指令 - 无需 jQuery 的 Twitter Bootstrap
  • 以及更多

我喜欢批评,它让我变得更好,所以我期待您的评论。

历史

v 1.0 - 2014-08-10 21:30 GMT

© . All rights reserved.