AngularJS 和 ASP.NET MVC 入门 - 期待已久!第三部分
如何在 ASP.NET MVC 上快速上手 AngularJS:第三部分
系列文章
引言
我早在八月份就写了本系列的第一部分和第二部分,并一直打算写第三部分,但总是因为这样那样的原因(闪电!)而分心,但现在终于开始了。
第三部分我们将要做什么
- 使用 ASP.Net Web API 构建 RESTful API 的基础,包括身份验证、角色等,然后创建一个连接到此 API 的新 Angular 应用。
源代码
本文配套的源代码可以在 这里找到。
RESTful API
好的,让我们开始构建 RESTful API。在 Visual Studio 中,选择 **新建项目...** => **Web** => **ASP.Net Web Application**(命名为 *Awesome Angular Web App 2.0*)=> Web API。身份验证保持 **Individual User Accounts**,然后单击“确定”。
现在,如果您还没有安装,我想让您安装三个应用程序
安装好 Postman 后,在 Chrome 中浏览以下 URL
chrome-extension://fdmmgilgnpjigdojojpjoooidkmcomcm/index.html
如果您熟悉 ASP.Net MVC,Web API 在某些方面会相对容易上手,因为它基于相同的技术,并且也是基于 MVC 等,但在其他方面(从需要学习 HTTP 和 RESTful API 是什么以及如何构建它们的角度来看)的学习曲线会更陡峭。这不是本篇文章的主题,但我建议阅读一本关于此主题的好书。
按 F5(或您调试快捷键)启动我们的 Web API 项目。您应该会看到类似以下内容
单击 API。当我们构建 API 时,此页面会自动更新有关我们所有端点以及相关 HTTP 请求类型的信息。单击此处的方法将为您提供有关该特定端点的详细信息,包括参数等。在某种程度上,它是自文档化的。
在网站仍然运行时,启动 Postman。我们将创建一个用户并登录。Web API 使用令牌(token)而不是 cookie 进行身份验证。它的工作原理很简单:如果您使用有效的用户名和密码登录,API 将返回一个令牌,以及该令牌的有效期限信息。您将此令牌作为标头添加到您所有后续的 HTTP 请求中,服务器便会知道您是谁。
就像普通的 ASP.Net 身份验证一样,您可以使用 **[Authenticate]** 属性来限制对控制器或方法的访问。您还可以创建角色,并允许不同的用户根据其角色访问 API 的不同部分。
让我们创建一个用户。查看 URL 来确定您的 API 正在运行的端口(它会显示 localhost,冒号,端口号),然后在 Postman 的 URL 栏中输入以下内容,将 8888 替换为您的端口号
https://:8888/api/Account/register
在下面的栏中,单击 **raw**,然后在下面的文本框中粘贴以下 JSON(别担心,AngularJS 的内容很快就会来了,但我们需要先完成这个部分)
{ "Email": "email@email.com", "Password": "Password1!", "ConfirmPassword": "Password1!" }
我们还需要将此请求的内容类型设置为 json,因此请单击 **Headers**,然后添加一个名为 **Content-Type** 的新标头,其值为 **application/json**。最后,在下拉列表中将请求类型更改为 POST。
Postman 应该看起来像这样
发送。这需要几秒钟,因为 ASP.Net 正在后台创建数据库,但一旦完成,您应该会收到 200 OK 的响应,如下所示
如果您在 fiddler 中查看,您应该也能找到此请求。查看这里的不同选项卡/按钮来探索请求的详细信息。
现在我们已经创建了一个用户,让我们继续登录。将 URL 更改为
https://:8888/token
这仍然是一个 POST 请求,但现在我们想将数据模式更改为 **x-www-form-urlencoded**,所以单击此按钮。添加以下字段
- grant_type: password
- userName: email@email.com(或您注册的任何内容)
- password: Password1!(或注册时设置的任何密码)
Postman 现在应该看起来像这样
发送,然后会返回类似以下内容的内容
{ "access_token": "R-AejC88wImTKUulwlZBRsR620zXuHcrjV26UGObVjl5s9aqJIhs2hzt60CdLhL0hXNR-kyLTgrTfiMDV4JZJsmC1jV3MQHKcScsW6lYAMz1kegSyQiSfRHVj8W1E76x9uiHYJVIWhwA_RH7GkTn3K_Z0ugV_0qsSd1cWZ5qpqRedrS1vbHNIr7PR-FvAcKGA5c0S7ffadD8TP6N8OX8AyEg2t5rxppAeT2AlqlY3G5HdJqDkPgXQx5pL_xXRWkQCuOhIgUCm-6TDAksNf-EJ7HzPKD7nl7KU8Pd66rQO56p_vtq6eOO9OtgAmN8FviR-gNKGHCsz4udPrAKTExF_Ht4hBpbLoiGIXIbVUpzTeB-RMZUMMcRgByo4tCELjd41pV0mjaXHS6s7mTuwlgGmxiAU5AoYgNTXVOe9YegZMvjW_lAIUw0YlZ0m7RAiPOTTDlRzmV1ntm3YGvAN9h9_m027twqfGz5YsHsbh3RYW8", "token_type": "bearer", "expires_in": 1209599, "userName": "email@email.com", ".issued": "Sun, 16 Nov 2014 22:55:45 GMT", ".expires": "Sun, 30 Nov 2014 22:55:45 GMT" }
RESTful API 和 Web API,一个非常有限的入门指南
当我们创建 Web API 项目时,它为我们创建了一个名为 **ValuesController** 的控制器,其中包含五个方法
- Get(重载了一个接受参数的版本)
- Post
- 将
- 删除
控制器的名称对应于 URL(就像在 ASP.Net MVC 中一样),方法的名称对应于请求的 HTTP 动词。因此,如果您想编写一个 HTTP GET 请求的端点,您就创建一个名为 Get 的方法,例如。
如何构建 RESTful API 的架构超出了本文的范围,但通常您会使用
- GET 请求来获取某些内容
- POST 来做某事(执行操作)
- PUT 来将某物放在某处(例如数据库)
- DELETE 来删除
您总是会使用一个表示结果的状态码来响应 HTTP 请求。当我们注册和登录时,每次都得到了 200 的响应。请随时在 Postman 中尝试这些请求(所有内容都保存在历史记录中),提供不同的值,删除/添加值等,以查看在不同场景下返回的不同状态码。例如,如果您在登录时提供了错误的密码,您会得到 **400 Bad Request** 的响应。
维基百科上关于此的页面很好地解释了各种状态码。阅读完之后,我建议您 到这里阅读关于 7xx 系列,您会笑的。在阅读完维基百科文章后,您会需要它。
所有 HTTP 请求都可以包含标头,无论其 HTTP 动词(Get、Post 等)是什么,有些还可以包含正文,而有些则不能。例如,POST 可以包含正文,而 GET 不能。在 Web API 中,此正文使用强类型 C# 对象构建并从方法返回。
让我们尝试调用此 values 控制器的 Get 方法。在 Postman 中,将 HTTP 操作类型更改为下拉列表中的 GET,并将 URL 更改为如下所示
https://:8888/api/values
暂时将 Content-Type 保留为 **application/json**。Postman 应该看起来像这样
发送,然后我们从 API 收到 401 Unauthorized 响应。这是因为 Values 控制器受到 **[Authorize]** 属性的保护!哎呀!
在 Postman 中,添加一个名为 **Authorization** 的新标头,其值为 **Bearer**,后跟一个空格,然后跟我们之前收到的令牌。Postman 现在应该看起来像这样
再次发送,成功!我们从 API 收到了以下 JSON
[ "value1", "value2" ]
尝试在 Postman 中调用 values 控制器中的每个方法,并修改方法以返回不同类型的对象。您也可以在 Postman 中看到所有这些操作,以及有关每个请求的详细信息。是不是很酷?
这原本是一篇关于 AngularJS 的文章,但我们还没有写任何 AngularJS 的内容!快点开始写 AngularJS 吧!
好吧,好吧。这次,我们不创建 ASP.Net MVC 应用程序来托管我们的网站,我们将使用纯粹的 CSS、HTML 和 Javascript 以及一点 Node.JS 来创建一切。
我之前让您安装 Node.JS。由于当前版本的 Node.JS 中存在一个 bug,您还需要在以下位置创建一个名为 **npm** 的目录
C:\Users\{{your user name}}\AppData\Roaming
当您阅读本文时,这可能已经修复,如果是这样,请忽略(并可以留下评论)。
创建一个目录用于您的网站,并使用您喜欢的文本编辑器(我使用的是 Atom)创建一个包含以下内容的 **index.html** 文件
<!DOCTYPE html> <html> <head> <title>Awesome Angular Web App 2.0</title> </head> <body> Hello Again! </body> </html>
现在打开 **Node.js command prompt**,然后运行以下命令
npm install http-server -g
这将安装一个名为 **http-server** 的轻量级 Web 服务器,它内置于 Javascript 中,我们可以用它来运行我们的 Angular 应用。**-g** 标志表示全局安装。这意味着服务器被安装到您的 PC 上,并且可以在任何地方运行。如果您省略此选项,服务器将被安装到您当前所在的目录。
现在导航到存放您网站的目录,并运行 http-server。您的窗口应该看起来像这样
在您的浏览器中,浏览到 **https://:8080/**。
难道这不很棒吗?
添加一个名为 **Scripts** 的子目录,以及一个名为 **Styles** 的子目录。我们在前两部分已经讲过这些内容了,所以我不会在这里做太多解释,只需复制粘贴我创建的每个文件的内容即可。
index.html
<!DOCTYPE html> <html> <head> <title>Awesome Angular Web App 2.0</title> </head> <body ng-app="AwesomeAngularWebApp" ng-controller="BaseController"> {{helloAgain}} <script src="//cdnjs.cloudflare.com/ajax/libs/angular.js/1.3.0/angular.min.js"></script> <script src="/Scripts/Controllers/BaseController.js"></script> <script src="/Scripts/AwesomeAngularWebApp.js"></script> </body> </html>
Scripts/AwesomeAngularWebApp.js
var AwesomeAngularWebApp = angular.module('AwesomeAngularWebApp', []); AwesomeAngularWebApp.controller('BaseController', BaseController);
Scripts/Controllers/BaseController.js
var BaseController = function($scope){ $scope.helloAgain = 'Hello Angular 1.3!'; } BaseController.$inject = ['$scope'];
https://:8080/ 现在应该看起来像这样
我们需要一个地方来存储从 API 返回的身份验证令牌。我们可以将其存储在服务中。AngularJS 中的服务是单例的,当应用启动时会创建它们的一个实例,并且您可以通过依赖注入将此实例传递给其他地方。它们是存储应用多个部分所需值的绝佳位置。
Scripts/Services/SessionService.js
var AuthenticationService = function(){ this.token = undefined; }
Scripts/AwesomeAngularWebApp.js
var AwesomeAngularWebApp = angular.module('AwesomeAngularWebApp', []); AwesomeAngularWebApp.controller('BaseController', BaseController); AwesomeAngularWebApp.service('SessionService', SessionService);
让我们创建工厂来进行登录和注册
Scripts/Factories/LoginFactory.js
var LoginFactory = function($http, $q, SessionService){ return function(username, password){ var result = $q.defer(); var params = {grant_type: "password", userName: username, password: password}; $http({ method: 'POST', url: SessionService.apiUrl + '/token', transformRequest: function(obj) { var str = []; for(var p in obj) str.push(encodeURIComponent(p) + "=" + encodeURIComponent(obj[p])); return str.join("&"); }, data: params, headers: {'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8;'} }) .success(function(response){ result.resolve(response); }) .error(function(response){ result.reject(response); }); return result.promise; } } LoginFactory.$inject = ['$http', '$q', 'SessionService'];
Scripts/Factories/RegisterFactory.js
var RegisterFactory = function($http, $q, SessionService){ return function(email, password, confirmPassword){ var result = $q.defer(); $http({ method: 'POST', url: SessionService.apiUrl + '/api/Account/register', data: {Email: email, Password: password, ConfirmPassword: confirmPassword}, headers: {'Content-Type': 'application/json'} }) .success(function(response){ result.resolve(response); }) .error(function(response){ result.reject(response); }); return result.promise; } } RegisterFactory.$inject = ['$http', '$q', 'SessionService'];
Scripts/AwesomeAngularWebApp.js
var AwesomeAngularWebApp = angular.module('AwesomeAngularWebApp', []); AwesomeAngularWebApp.controller('BaseController', BaseController); AwesomeAngularWebApp.service('SessionService', SessionService); AwesomeAngularWebApp.factory('LoginFactory', LoginFactory); AwesomeAngularWebApp.factory('RegisterFactory', RegisterFactory);
现在我将为登录和注册视图(我们稍后会创建)创建控制器。
Scripts/Controllers/LoginController.js
var LoginController = function($scope, $location, LoginFactory, SessionService){ $scope.loginForm = { username: undefined, password: undefined, errorMessage: undefined }; $scope.login = function(){ LoginFactory($scope.loginForm.username, $scope.loginForm.password) .then(function(response){ SessionService.token = response.access_token; $location.path('/'); }, function(response){ $scope.loginForm.errorMessage = response.error_description; }); } } LoginController.$inject = ['$scope', '$location', 'LoginFactory', 'SessionService'];
Scripts/Controllers/RegisterController.js
var RegisterController = function($scope, LoginFactory, RegisterFactory, SessionService){ $scope.registerForm = { username: undefined, password: undefined, confirmPassword: undefined, errorMessage: undefined }; $scope.register = function(){ RegisterFactory($scope.registerForm.username, $scope.registerForm.password, $scope.registerForm.confirmPassword) .then(function(){ LoginFactory($scope.registerForm.username, $scope.registerForm.password) .then(function(response){ SessionService.token = response.access_token; }, function(response){ $scope.registerForm.errorMessage = response; }); }, function(response){ $scope.registerForm.errorMessage = response; }); } } RegisterController.$inject = ['$scope', 'LoginFactory', 'RegisterFactory', 'SessionService'];
Scripts/AwesomeAngularWebApp.js
var AwesomeAngularWebApp = angular.module('AwesomeAngularWebApp', []); AwesomeAngularWebApp.controller('BaseController', BaseController); AwesomeAngularWebApp.controller('LoginController', LoginController); AwesomeAngularWebApp.controller('RegisterController', RegisterController); AwesomeAngularWebApp.service('SessionService', SessionService); AwesomeAngularWebApp.factory('LoginFactory', LoginFactory); AwesomeAngularWebApp.factory('RegisterFactory', RegisterFactory);
以及这些控制器对应的视图,并更新路由代码。
Views/Login.html
<form role="form" ng-submit="login()"> <div class="form-group"> <label for="emailAddress">Email address</label> <input type="email" class="form-control" id="emailAddress" placeholder="Enter email" ng-model="loginForm.username"> </div> <div class="form-group"> <label for="password">Password</label> <input type="password" class="form-control" id="password" placeholder="Password" ng-model="loginForm.password"> </div> <p class="help-block" ng-if="loginForm.errorMessage">{{loginForm.errorMessage}}</p> </div> <button type="submit" class="btn btn-default">Login</button> </form>
Views/Register.html
<form role="form" ng-submit="register()"> <div class="form-group"> <label for="emailAddress">Email address</label> <input type="email" class="form-control" id="emailAddress" placeholder="Enter email" ng-model="registerForm.username"> </div> <div class="form-group"> <label for="password">Password</label> <input type="password" class="form-control" id="password" placeholder="Password" ng-model="registerForm.password"> </div> <div class="form-group"> <label for="password">Confirm Password</label> <input type="password" class="form-control" id="password" placeholder="Confirm Password" ng-model="registerForm.confirmPassword"> </div> <p class="help-block" ng-if="registerForm.errorMessage">{{registerForm.errorMessage}}</p> </div> <button type="submit" class="btn btn-default">Register</button> </form>
Scripts/AwesomeAngularWebApp.js
var AwesomeAngularWebApp = angular.module('AwesomeAngularWebApp', ['ngRoute']); AwesomeAngularWebApp.controller('BaseController', BaseController); AwesomeAngularWebApp.controller('LoginController', LoginController); AwesomeAngularWebApp.controller('RegisterController', RegisterController); AwesomeAngularWebApp.service('SessionService', SessionService); AwesomeAngularWebApp.factory('LoginFactory', LoginFactory); AwesomeAngularWebApp.factory('RegisterFactory', RegisterFactory); var ConfigFunction = function($routeProvider) { $routeProvider .when('/login', { templateUrl: 'views/login.html', controller: 'LoginController' }) .when('/register', { templateUrl: 'views/register.html', controller: 'RegisterController' }); }; ConfigFunction.$inject = ['$routeProvider']; AwesomeAngularWebApp.config(ConfigFunction);
index.html
<!DOCTYPE html> <html> <head> <title>Awesome Angular Web App 2.0</title> <link rel="stylesheet" href="//cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/3.3.1/css/bootstrap.min.css"> </head> <body ng-app="AwesomeAngularWebApp" ng-controller="BaseController"> {{helloAgain}} <a href="/#/login" ng-if="!loggedIn()">Login</a> <a href="/#/register" ng-if="!loggedIn()">Register</a> <span class="label label-success" ng-if="loggedIn()">Logged In</span> <div ng-view></div> <script src="//cdnjs.cloudflare.com/ajax/libs/angular.js/1.3.0/angular.min.js"></script> <script src="//cdnjs.cloudflare.com/ajax/libs/angular.js/1.3.0/angular-route.min.js"></script> <script src="/Scripts/Controllers/BaseController.js"></script> <script src="/Scripts/Controllers/LoginController.js"></script> <script src="/Scripts/Controllers/RegisterController.js"></script> <script src="/Scripts/Services/SessionService.js"></script> <script src="/Scripts/Factories/LoginFactory.js"></script> <script src="/Scripts/Factories/RegisterFactory.js"></script> <script src="/Scripts/AwesomeAngularWebApp.js"></script> </body> </html>
Scripts/BaseController.js
var BaseController = function($scope, SessionService){ $scope.helloAgain = 'Hello Angular 1.3!'; $scope.loggedIn = function(){ return SessionService.token !== undefined; } } BaseController.$inject = ['$scope', 'SessionService'];
好的,让我们回到 **https://:8080/**,确保一切都已正确连接。您应该能够浏览到登录和注册视图。检查您的浏览器控制台,如果有错误,您可能错过了一个步骤(或者我出错了,如果是这样,请在评论中告诉我)。
让我们尝试登录
为了避免您向上滚动,这里是之前创建的用户(除非您更改了它们)的用户名和密码(否则向上滚动也无济于事)。
- userName: email@email.com
- password: Password1!
让我们浏览到登录表单,并尝试登录。哦不
我们的 API 在一个服务器上,而我们的网站在另一个服务器上,因此登录请求因安全原因被浏览器阻止了。我们可以通过 CORS 来解决这个问题。
回到 Visual Studio,从 **View** => **Other Windows** => **Package Manager Console** 打开 nuget 包管理器控制台。运行以下命令
Install-Package Microsoft.Owin.Cors
在 Startup.cs 中,修改 Configuration 使其包含以下内容:
public void Configuration(IAppBuilder app) { app.UseCors(Microsoft.Owin.Cors.CorsOptions.AllowAll); ConfigureAuth(app); }
让我们再次尝试登录,首先尝试一个故意错误的用户名/密码组合。
很好,我们从服务器收到了 400 响应,并在屏幕上显示了错误消息。现在让我们正确登录。
太棒了!我们回到了主页,令牌已存储在服务中,登录和注册链接也消失了。
现在我们将编写一个工厂来从我们 API 中的 ValuesController 类 **Get** **Values**。
Scripts/Factories/GetValuesFactory.js
var GetValuesFactory = function($http, $q, SessionService){ return function(){ var result = $q.defer(); $http({ method: 'GET', url: SessionService.apiUrl + '/api/Values', headers: {'Content-Type': 'application/json', 'Authorization': 'Bearer ' + SessionService.token} }) .success(function(response){ result.resolve(response); }) .error(function(response){ result.reject(response); }); return result.promise; } } GetValuesFactory.$inject = ['$http', '$q', 'SessionService'];
一个用于我们稍后将创建的视图的控制器。
Scripts/Controllers/ValuesController.js
var ValuesController = function($scope, GetValuesFactory, SessionService){ $scope.values = []; $scope.error = { message: undefined }; $scope.getValues = function(){ GetValuesFactory() .then(function(response){ $scope.values = response; }, function(response){ $scope.error.message = response.Message; }); } } ValuesController.$inject = ['$scope', 'GetValuesFactory', 'SessionService'];
一个视图。
Views/values.html
<button ng-click="getValues()">Get Values</button> <ul class="nav nav-pills" ng-if="values.length > 0"> <li ng-repeat="value in values">{{value}}<li> </ul> <p class="help-block" ng-if="error.message">{{error.message}}</p>
更新的路由和新内容的注册。
Scripts/AwesomeAngularWebApp.js
var AwesomeAngularWebApp = angular.module('AwesomeAngularWebApp', ['ngRoute']); AwesomeAngularWebApp.controller('BaseController', BaseController); AwesomeAngularWebApp.controller('LoginController', LoginController); AwesomeAngularWebApp.controller('RegisterController', RegisterController); AwesomeAngularWebApp.controller('ValuesController', ValuesController); AwesomeAngularWebApp.service('SessionService', SessionService); AwesomeAngularWebApp.factory('LoginFactory', LoginFactory); AwesomeAngularWebApp.factory('RegisterFactory', RegisterFactory); AwesomeAngularWebApp.factory('GetValuesFactory', GetValuesFactory); var ConfigFunction = function($routeProvider) { $routeProvider .when('/login', { templateUrl: 'views/login.html', controller: 'LoginController' }) .when('/register', { templateUrl: 'views/register.html', controller: 'RegisterController' }) .when('/values', { templateUrl: 'views/values.html', controller: 'ValuesController' }); }; ConfigFunction.$inject = ['$routeProvider']; AwesomeAngularWebApp.config(ConfigFunction);
一个链接,以及用于新控制器/工厂的脚本标签。
<!DOCTYPE html> <html> <head> <title>Awesome Angular Web App 2.0</title> <link rel="stylesheet" href="//cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/3.3.1/css/bootstrap.min.css"> </head> <body ng-app="AwesomeAngularWebApp" ng-controller="BaseController"> {{helloAgain}} <a href="/#/login" ng-if="!loggedIn()">Login</a> <a href="/#/register" ng-if="!loggedIn()">Register</a> <a href="/#/values">Values</a> <span class="label label-success" ng-if="loggedIn()">Logged In</span> <div ng-view></div> <script src="//cdnjs.cloudflare.com/ajax/libs/angular.js/1.3.0/angular.min.js"></script> <script src="//cdnjs.cloudflare.com/ajax/libs/angular.js/1.3.0/angular-route.min.js"></script> <script src="/Scripts/Controllers/BaseController.js"></script> <script src="/Scripts/Controllers/LoginController.js"></script> <script src="/Scripts/Controllers/RegisterController.js"></script> <script src="/Scripts/Controllers/ValuesController.js"></script> <script src="/Scripts/Services/SessionService.js"></script> <script src="/Scripts/Factories/LoginFactory.js"></script> <script src="/Scripts/Factories/RegisterFactory.js"></script> <script src="/Scripts/Factories/GetValuesFactory.js"></script> <script src="/Scripts/AwesomeAngularWebApp.js"></script> </body> </html>
好的,让我们启动一切。首先,导航到我们新的 values 视图并单击按钮。Web API 返回一个 401(未授权)HTTP 错误,该错误显示在屏幕上。登录并再次单击按钮,一切都会按预期工作。
别忘了我
目前,如果您登录,然后刷新页面,您将被踢出,需要重新进行身份验证。我们可以通过使用 AngularJS 的 **ng-cookies** 模块将令牌存储在 cookie 中来解决这个问题。更新 index.html 以从 Cloudflare 拉取(在 **angular-route** 标签之后添加)。
<script src="//cdnjs.cloudflare.com/ajax/libs/angular.js/1.3.0/angular-cookies.min.js"></script>
更新 AwesomeAngularWebApp.js 以将此模块注册到我们的应用中。
var AwesomeAngularWebApp = angular.module('AwesomeAngularWebApp', ['ngRoute', 'ngCookies']);
更新 SessionService 以获取令牌并将其存储在 cookie 中。
var SessionService = function($cookies){ this.token = undefined; this.getToken = function(){ if(!$cookies.awesomeAngularWebAppToken){ if(!this.token){ return undefined; } this.setToken(this.token); } return $cookies.awesomeAngularWebAppToken; } this.setToken = function(token){ this.token = token; $cookies.awesomeAngularWebAppToken = token; } this.apiUrl = 'https://:5586'; } SessionService.$inject = ['$cookies'];
LoginController、RegisterController、BaseController 和 GetValuesFactory 调用新的 getter 和 setter 方法。
Scripts/Controllers/LoginController.js
var LoginController = function($scope, $location, LoginFactory, SessionService){ $scope.loginForm = { username: undefined, password: undefined, errorMessage: undefined }; $scope.login = function(){ LoginFactory($scope.loginForm.username, $scope.loginForm.password) .then(function(response){ SessionService.setToken(response.access_token); $location.path('/'); }, function(response){ $scope.loginForm.errorMessage = response.error_description; }); } } LoginController.$inject = ['$scope', '$location', 'LoginFactory', 'SessionService'];
Scripts/Controllers/RegisterController.js
var RegisterController = function($scope, LoginFactory, RegisterFactory, SessionService){ $scope.registerForm = { username: undefined, password: undefined, confirmPassword: undefined, errorMessage: undefined }; $scope.register = function(){ RegisterFactory($scope.registerForm.username, $scope.registerForm.password, $scope.registerForm.confirmPassword) .then(function(){ LoginFactory($scope.registerForm.username, $scope.registerForm.password) .then(function(response){ SessionService.setToken(response.access_token); }, function(response){ $scope.loginForm.errorMessage = response; }); }, function(response){ $scope.registerForm.errorMessage = response; }); } } RegisterController.$inject = ['$scope', 'LoginFactory', 'RegisterFactory', 'SessionService'];
Scripts/Controllers/BaseController.js
var BaseController = function($scope, SessionService){ $scope.helloAgain = 'Hello Angular 1.3!'; $scope.loggedIn = function(){ return SessionService.getToken() !== undefined; } } BaseController.$inject = ['$scope', 'SessionService'];
Scripts/Factories/GetValuesFactory.js
var GetValuesFactory = function($http, $q, SessionService){ return function(){ var result = $q.defer(); $http({ method: 'GET', url: SessionService.apiUrl + '/api/Values', headers: {'Content-Type': 'application/json', 'Authorization': 'Bearer ' + SessionService.getToken()} }) .success(function(response){ result.resolve(response); }) .error(function(response){ result.reject(response); }); return result.promise; } } GetValuesFactory.$inject = ['$http', '$q', 'SessionService'];
现在,当您登录并刷新页面时,您仍然登录着!万岁!
角色
正如我之前提到的,不同的用户可以拥有不同的角色。我发现使用 SQL 设置角色是最简单的方法。我们现在就来做。
打开 SQL Server Management Studio,然后将 **Server name** 输入为 **(localdb)\v11.0**,如下所示,然后单击连接。
您应该会看到一个名为 **aspnet-Awesome Angular Web App 2.0-20141116095710** 的数据库,最后的数字只是创建此数据库的时间戳。
让我们创建一个名为 SuperAdmin 的角色,并将我们创建的用户分配给此角色。使用以下 SQL 更新 AspNetRoles 表。
INSERT INTO [aspnet-Awesome Angular Web App 2.0-20141116095710].[dbo].[AspNetRoles] VALUES('8138aad6-dfdb-422d-862d-8d5acab5c8a1', 'SuperAdmin')
现在让我们更新我们的 ValuesController,使其只允许 SuperAdmin **Get** 值。像这样修改控制器上的 [Authorize] 属性。
[Authorize(Roles = "SuperAdmin")]
重要提示:当我们停止并重新启动 API 时,我们所有的令牌都将失效,因为它们存储在内存中,所以您需要同时清除浏览器的缓存。
现在,当我们访问我们的站点并登录时(使用以下凭据,方便懒人),
- userName: email@email.com
- password: Password1!
我们得到了 401 未授权响应。这是因为我们不是 SuperAdmin 组的成员。让我们修复这个问题,首先针对 [AspNetUsers] 运行以下 SQL 来查看我们的 ID 是什么。
SELECT [Id] FROM [aspnet-Awesome Angular Web App 2.0-20141116095710].[dbo].[AspNetUsers] WHERE [Email] = 'email@email.com'
对我来说,ID 是 **e79c2336-265e-42d0-a62c-df44a3dfa0f4**,您的 ID 会不同,所以请用您的 ID 替换下面的 SQL 中的 ID。下面的 SQL 将用户添加到该角色。
INSERT INTO [aspnet-Awesome Angular Web App 2.0-20141116095710].[dbo].[AspNetUserRoles] VALUES('e79c2336-265e-42d0-a62c-df44a3dfa0f4', '8138aad6-dfdb-422d-862d-8d5acab5c8a1')
您现在是超级管理员了,但登录时不是,所以请清除浏览器缓存,重新登录,然后重试。如果一切顺利,您将能够成功地对 Values 控制器执行 HTTP 请求,但其他非 SuperAdmin 角色的用户将无法执行。
回顾
今天,我们使用 ASP.Net Web API 创建了一个 RESTful API,一个运行在 node.js 服务器上的 AngularJS 应用程序,并成功配置了应用程序以对 API 进行身份验证,将令牌存储在 cookie 中。我们还创建了一个角色,并将 API 的某个区域限制给该角色内的用户。
第四部分预告
您想看到什么?请留言。
安全
Microsoft 的 Troy Hunt 使用本文提供的代码作为 Pluralsight 上客户端框架安全课程的基础,该课程可在 此处找到。我强烈推荐您查看一下。
评论/批评等
欢迎在文章下方留下您的评论/批评/问题等,我都会回复。感谢您的阅读