chsakell分享了一个前端使用AngularJS,后端使用ASP.NET Web API的项目。

源码: https://github.com/chsakell/spa-webapi-angularjs
文章:http://chsakell.com/2015/08/23/building-single-page-applications-using-web-api-and-angularjs-free-e-book/

这里记录下对此项目的理解。分为如下几篇:

● 对一个前端使用AngularJS后端使用ASP.NET Web API项目的理解(1)--领域、Repository、Service

● 对一个前端使用AngularJS后端使用ASP.NET Web API项目的理解(2)--依赖倒置、Bundling、视图模型验证、视图模型和领域模型映射、自定义handler

● 对一个前端使用AngularJS后端使用ASP.NET Web API项目的理解(3)--主页面布局

● 对一个前端使用AngularJS后端使用ASP.NET Web API项目的理解(4)--Movie增改查以及上传图片

Home/Index.cshtml视图

创建一个有关ui的module:spa/modules/common.ui.js

(function () {'use strict';angular.module('common.ui', ['ui.bootstrap', 'chieffancypants.loadingBar']);})();

创建一个有关功能的module:spa/modules/common.ui.js

(function () {'use strict';angular.module('common.core', ['ngRoute', 'ngCookies', 'base64', 'angularFileUpload', 'angularValidator', 'angucomplete-alt']);})();

Home/Index.cshtml视图摘要:

<html ng-app="homeCinema"><body ng-controller="rootCtrl"><top-bar></top-bar><side-bar></side-bar><div class="page {{ pageClass }}" ng-view></div></body>
</html>

homeCinema是一个主module,依赖common.ui和common.core这2个module,定义在了spa/app.js中,具体如下:

//config传入名称为config的函数
//run传入名称为run的函数
//执行顺序:app.config()→app.run()→directive compile functions if found→app.controller→directive's link funciton if found
angular.module('homeCinema', ['common.core', 'common.ui']).config(config).run(run);//为config函数注入参数
config.$inject = ['$routeProvider'];//路由设置,让controller和页面匹配
function config($routeProvider) {$routeProvider.when("/", {templateUrl: "scripts/spa/home/index.html",controller: "indexCtrl"}).when("/login", {templateUrl: "scripts/spa/account/login.html",controller: "loginCtrl"}).when("/register", {templateUrl: "scripts/spa/account/register.html",controller: "registerCtrl"}).when("/customers", {templateUrl: "scripts/spa/customers/customers.html",controller: "customersCtrl"}).when("/customers/register", {templateUrl: "scripts/spa/customers/register.html",controller: "customersRegCtrl",//注入到cotroller中的依赖,controller会等到resolve的动作结束后再初始化
            resolve: { isAuthenticated: isAuthenticated }}).when("/movies", {templateUrl: "scripts/spa/movies/movies.html",controller: "moviesCtrl"}).when("/movies/add", {templateUrl: "scripts/spa/movies/add.html",controller: "movieAddCtrl",resolve: { isAuthenticated: isAuthenticated }}).when("/movies/:id", {templateUrl: "scripts/spa/movies/details.html",controller: "movieDetailsCtrl",resolve: { isAuthenticated: isAuthenticated }}).when("/movies/edit/:id", {templateUrl: "scripts/spa/movies/edit.html",controller: "movieEditCtrl"}).when("/rental", {templateUrl: "scripts/spa/rental/rental.html",controller: "rentStatsCtrl"}).otherwise({ redirectTo: "/" });
}    //为resolve的函数注入参数
isAuthenticated.$inject = ['membershipService', '$rootScope', '$location'];//resolve执行的函数
function isAuthenticated(membershipService, $rootScope, $location) {if (!membershipService.isUserLoggedIn()) {$rootScope.previousState = $location.path();$location.path('/login');}
}//为run函数注入参数
run.$inject = ['$rootScope', '$location', '$cookieStore', '$http'];//一些初始化工作
function run($rootScope, $location, $cookieStore, $http) {// handle page refreshes//rootScope.repository//rootScope.repository.loggedUser$rootScope.repository = $cookieStore.get('repository') || {};if ($rootScope.repository.loggedUser) {$http.defaults.headers.common['Authorization'] = $rootScope.repository.loggedUser.authdata;}$(document).ready(function () {$(".fancybox").fancybox({openEffect: 'none',closeEffect: 'none'});$('.fancybox-media').fancybox({openEffect: 'none',closeEffect: 'none',helpers: {media: {}}});$('[data-toggle=offcanvas]').click(function () {$('.row-offcanvas').toggleClass('active');});});
}

side-bar

在界面中的适用方法:<side-bar></side-bar>

在common.ui这个module中自定义了一个directive。在spa/layout/sideBar.directive.js

(function(app) {'use strict';app.directive('sideBar', sideBar);function sideBar() {return {restrict: 'E',replace: true,templateUrl: '/scripts/spa/layout/sideBar.html'}}})(angular.module('common.ui'));

spa/layout/sidebar.html摘要:

<a ng-href="#/">Home</i>
<a ng-href="#/customers/">Customers</a>
<a ng-href="#/customers/register">Register customer</a>
<a ng-href="#/movies/">Movies</a>
<a ng-href="#/movies/add">Add movie<i class="fa fa-plus-circle fa-fw pull-right"></i></a>
<a ng-href="#/rental/">Rental history<i class="fa fa-leanpub fa-fw pull-right"></i></a>
<a ng-href="#/login" ng-if="!userData.isUserLoggedIn">Login</a>
<a ng-click="logout();" ng-if="userData.isUserLoggedIn">Logout</a>

其中,userData.isUserLoggedIn肯定是homeCinema这个module的controller,定义在了spa/home/rootCtrl.js中。

(function (app) {'use strict';app.controller('rootCtrl', rootCtrl);rootCtrl.$inject = ['$scope','$location', 'membershipService','$rootScope'];function rootCtrl($scope, $location, membershipService, $rootScope) {//userData对象$scope.userData = {};//$scope.userData.displayUserInfo方法显示用户$scope.userData.displayUserInfo = displayUserInfo;//方法登出$scope.logout = logout;//$scope.userData.isUserLoggedIn,布尔类型//$scope.usernamefunction displayUserInfo() {$scope.userData.isUserLoggedIn = membershipService.isUserLoggedIn();if($scope.userData.isUserLoggedIn){//主页面初始化的时候就定义在$rootScope.repository.loggedUser.username了$scope.username = $rootScope.repository.loggedUser.username;}}function logout() {membershipService.removeCredentials();$location.path('#/');$scope.userData.displayUserInfo();}$scope.userData.displayUserInfo();}})(angular.module('homeCinema'));

以上,注入了membershipService这个服务,另外还有一个apiService,notificationService等服务被放在了common.core模块中,都是以factory的方式创建的服务。

spa/services/notificationService.js这个服务基于toastr.js管理所有的通知。

(function (app) {'use strict';//以工厂的方式创建服务app.factory('notificationService', notificationService);function notificationService() {toastr.options = {"debug": false,"positionClass": "toast-top-right","onclick": null,"fadeIn": 300,"fadeOut": 1000,"timeOut": 3000,"extendedTimeOut": 1000};var service = {displaySuccess: displaySuccess,displayError: displayError,displayWarning: displayWarning,displayInfo: displayInfo};return service;function displaySuccess(message) {toastr.success(message);}function displayError(error) {if (Array.isArray(error)) {error.forEach(function (err) {toastr.error(err);});} else {toastr.error(error);}}function displayWarning(message) {toastr.warning(message);}function displayInfo(message) {toastr.info(message);}}})(angular.module('common.core'));

spa/services/apiService.js服务用来管理GET和POST请求。

(function (app) {'use strict';app.factory('apiService', apiService);apiService.$inject = ['$http', '$location', 'notificationService','$rootScope'];function apiService($http, $location, notificationService, $rootScope) {var service = {get: get,post: post};function get(url, config, success, failure) {return $http.get(url, config).then(function (result) {success(result);}, function (error) {if (error.status == '401') {notificationService.displayError('Authentication required.');$rootScope.previousState = $location.path();$location.path('/login');}else if (failure != null) {failure(error);}});}function post(url, data, success, failure) {return $http.post(url, data).then(function (result) {success(result);}, function (error) {if (error.status == '401') {notificationService.displayError('Authentication required.');$rootScope.previousState = $location.path();$location.path('/login');}else if (failure != null) {failure(error);}});}return service;}})(angular.module('common.core'));

top bar

自定义的top bar放在了common.ui模块中,spa/layout/topBar.directive.js

(function(app) {'use strict';app.directive('topBar', topBar);function topBar() {return {restrict: 'E',replace: true,templateUrl: '/scripts/spa/layout/topBar.html'}}})(angular.module('common.ui'));

spa/layout/topBar.html摘要:

<a class="navbar-brand active" href="#/">Home Cinema</a>
<a href="#about">About</a>
<ulng-if="userData.isUserLoggedIn"><a href="#/">{{username}}</a>
</ul>

以上,username, userData.isUserLoggedIn都是homeCinema这个模块中rootCtrl控制器的变量。

Latest Movies 

首先要写一个继承ApiController的类,用来处理登录异常。

namespace HomeCinema.Web.Infrastructure.Core
{public class ApiControllerBase : ApiController{protected readonly IEntityBaseRepository<Error> _errorsRepository;protected readonly IUnitOfWork _unitOfWork;public ApiControllerBase(IEntityBaseRepository<Error> errorsRepository, IUnitOfWork unitOfWork){_errorsRepository = errorsRepository;_unitOfWork = unitOfWork;}public ApiControllerBase(IDataRepositoryFactory dataRepositoryFactory, IEntityBaseRepository<Error> errorsRepository, IUnitOfWork unitOfWork){_errorsRepository = errorsRepository;_unitOfWork = unitOfWork;}protected HttpResponseMessage CreateHttpResponse(HttpRequestMessage request, Func<HttpResponseMessage> function){HttpResponseMessage response = null;try{response = function.Invoke();}catch (DbUpdateException ex){LogError(ex);response = request.CreateResponse(HttpStatusCode.BadRequest, ex.InnerException.Message);}catch (Exception ex){LogError(ex);response = request.CreateResponse(HttpStatusCode.InternalServerError, ex.Message);}return response;}//把错误报错到数据库中去private void LogError(Exception ex){try{Error _error = new Error(){Message = ex.Message,StackTrace = ex.StackTrace,DateCreated = DateTime.Now};_errorsRepository.Add(_error);_unitOfWork.Commit();}catch { }}}
}

以上, ApiControllerBase定义了一个重要的方法CreateHttpResponse,不但可以处理请求响应,还可以进行异常处理,把异常记录到数据库。

接着定义MoviesController,继承ApiControllerBase基类。

[Authorize(Roles = "Admin")]
[RoutePrefix("api/movies")]
public class MoviesController : ApiControllerBase
{private readonly IEntityBaseRepository<Movie> _moviesRepository;public MoviesController(IEntityBaseRepository<Movie> moviesRepository,IEntityBaseRepository<Error> _errorsRepository, IUnitOfWork _unitOfWork): base(_errorsRepository, _unitOfWork){_moviesRepository = moviesRepository;}...
}

首页展示6个Moview,针对此写一个aciton方法。

[AllowAnonymous]
[Route("latest")]
public HttpResponseMessage Get(HttpRequestMessage request)
{return CreateHttpResponse(request, () =>{HttpResponseMessage response = null;//获取6个var movies = _moviesRepository.GetAll().OrderByDescending(m => m.ReleaseDate).Take(6).ToList();//转换成视图模型IEnumerable<MovieViewModel> moviesVM = Mapper.Map<IEnumerable<Movie>, IEnumerable<MovieViewModel>>(movies);//创建响应response = request.CreateResponse<IEnumerable<MovieViewModel>>(HttpStatusCode.OK, moviesVM);return response;});
}

界面如何展示出来呢?在spa/app.js中的路由已经有了定义。

 $routeProvider.when("/", {templateUrl: "scripts/spa/home/index.html",controller: "indexCtrl"})...

也就是在根地址下,使用indexCtrl这个controller,路由到scripts/spa/home/index.html这里。

scripts/spa/home/index.html

<div ng-if="loadingMovies"><label class="label label-primary">Loading movies...</label>
</div><div ng-repeat="movie in latestMovies"><strong>{{movie.Title}} </strong><a ng-href="../../Content/images/movies/{{movie.Image}}" title="{{movie.Description | limitTo:200}}"><img class="media-object" height="120" ng-src="../../Content/images/movies/{{movie.Image}}" alt="" /></a><available-movie is-available="{{movie.IsAvailable}}"></available-movie>{{movie.Description | limitTo: 70}}...</small><label class="label label-info">{{movie.Genre}}</label><span component-rating="{{movie.Rating}}"></span><a ng-href="{{movie.TrailerURI}}">Trailer<i class="fa fa-video-camera fa-fw"></i></a></div>

以上,其实定义了2个directive,一个是available-movie,用来显示Movie的状态,可能是Available,也可能是Not Available;另一个是component-rating,用来显示星级,基于 raty.js文件开发。

先来看控制器:spa/home/indexCtrl.js

(function (app) {'use strict';app.controller('indexCtrl', indexCtrl);indexCtrl.$inject = ['$scope','apiService', 'notificationService'];function indexCtrl($scope, apiService, notificationService) {$scope.loadingMovies = true;$scope.latestMovies = [];$scope.loadData = loadData;function loadData() {apiService.get('/api/movies/latest', null,moviesLoadCompleted,moviesLoadFailed);}function moviesLoadCompleted(result) {$scope.latestMovies = result.data;$scope.loadingMovies = false;}function moviesLoadFailed(response) {notificationService.displayError(response.data);}loadData();}})(angular.module('homeCinema'));

再来看是否显示Available Movie的这个自定义directive,在页面中是这样使用的:

<available-movie is-available="{{movie.IsAvailable}}"></available-movie>

实际是在spa/directives/availableMovie.directive.js中定义的。

(function (app) {'use strict';//注意这里的惯例,这里的availableMovie相当于界面上的available-movieapp.directive('availableMovie', availableMovie);function availableMovie() {return {restrict: 'E',templateUrl: "/Scripts/spa/directives/availableMovie.html",link: function ($scope, $element, $attrs) {//getAvailbleClass供html中调用//attrs表示属性,注意这里的惯例:isAvailable相当于界面上的is-available$scope.getAvailableClass = function () {if ($attrs.isAvailable === 'true')return 'label label-success'elsereturn 'label label-danger'};//getAvailability根据属性的布尔值返回不同的字符串$scope.getAvailability = function () {if ($attrs.isAvailable === 'true')return 'Available!'elsereturn 'Not Available'};}}}})(angular.module('common.ui'));

最终,在spa/directives/availableMovie.html中:

<label ng-class="getAvailableClass()">{{getAvailability()}}</label>

在Movie的显示中,还定义了一个directive,用来显示星级,在界面中按如下:

<span component-rating="{{movie.Rating}}"></span>

在spa/directives/componentRating.directive.js中:

(function(app) {'use strict';app.directive('componentRating', componentRating);function componentRating() {return {restrict: 'A', //A说明directive以属性的方式link: function ($scope, $element, $attrs) {$element.raty({score: $attrs.componentRating, //componentRating相当于界面中的component-rating,接收component-rating属性值halfShow: false,readOnly: $scope.isReadOnly,//表明是只读的星级noRatedMsg: "Not rated yet!",starHalf: "../Content/images/raty/star-half.png",starOff: "../Content/images/raty/star-off.png",starOn: "../Content/images/raty/star-on.png",hints: ["Poor", "Average", "Good", "Very Good", "Excellent"],click: function (score, event) {//Set the model value$scope.movie.Rating = score;$scope.$apply();}});}}}})(angular.module('common.ui'));

点击首页Movie缩略图,弹出窗口

关于缩略图的html部分就在scripts/spa/home/index.html中,具体为:

<a class="fancybox pull-left" rel="gallery1" ng-href="../../Content/images/movies/{{movie.Image}}" title="{{movie.Description | limitTo:200}}"><img class="media-object" height="120" ng-src="../../Content/images/movies/{{movie.Image}}" alt="" />
</a>

根据类名fancybox,使用jquery语法调用jquery.fancybox.js的语法,具体调用是在app.js中调用的:

function run($rootScope, $location, $cookieStore, $http) {...$(document).ready(function () {$(".fancybox").fancybox({openEffect: 'none',closeEffect: 'none'});...});
}

待续~

对一个前端使用AngularJS后端使用ASP.NET Web API项目的理解(3)相关推荐

  1. 购物车Demo,前端使用AngularJS,后端使用ASP.NET Web API(2)--前端,以及前后端Session

    原文:购物车Demo,前端使用AngularJS,后端使用ASP.NET Web API(2)--前端,以及前后端Session chsakell分享了前端使用AngularJS,后端使用ASP.NE ...

  2. 购物车Demo,前端使用AngularJS,后端使用ASP.NET Web API(3)--Idetity,OWIN前后端验证

    原文:购物车Demo,前端使用AngularJS,后端使用ASP.NET Web API(3)--Idetity,OWIN前后端验证 chsakell分享了前端使用AngularJS,后端使用ASP. ...

  3. 前端使用AngularJS的$resource,后端ASP.NET Web API,实现增删改查

    首页 > 技术 > 编程 > NET > 前端使用AngularJS的$resource,后端ASP.NET Web API,实现增删改查 前端使用AngularJS的$res ...

  4. 【转】在ASP.NET Web API 2中使用Owin基于Token令牌的身份验证

    基于令牌的身份验证 基于令牌的身份验证主要区别于以前常用的基于cookie的身份验证,基于cookie的身份验证在B/S架构中使用比较多,但是在Web Api中因其特殊性,基于cookie的身份验证已 ...

  5. 生成用于ASP.NET Web API的C#客户端API

    目录 介绍 主要特征 主要好处 背景 推定(Presumptions) 使用代码 步骤0:将NuGet软件包WebApiClientGen安装到Web MVC/API项目 步骤1:建立.NET Cli ...

  6. ASP.NET Web API实现简单的文件下载与上传

    ASP.NET Web API实现简单的文件下载与上传.首先创建一个ASP.NET Web API项目,然后在项目下创建FileRoot目录并在该目录下创建ReportTemplate.xlsx文件, ...

  7. 生成用于ASP.NET Web API的TypeScript客户端API

    目录 介绍 备注 背景 推定(Presumptions) 使用代码 步骤0:将NuGet package WebApiClientGen和WebApiClientGen.jQuery安装到Web AP ...

  8. ASP.NET Web API 2 入门教程

    译者:jiankunking 出处:http://blog.csdn.net/jiankunking 源码下载 HTTP不仅提供web页面服务,在构建公开服务和数据api方面,它也是一个强大的平台.H ...

  9. 【Web API系列教程】1.1 — ASP.NET Web API入门

    前言 HTTP不仅仅服务于web页面,同时也是构建暴露服务和数据的API的强大平台.HTTP有着简单.灵活和无处不在的特点.你能想到的几乎所有平台都包含有一个HTTP库,所以HTTP服务可以遍及广泛的 ...

  10. 利用查询条件对象,在Asp.net Web API中实现对业务数据的分页查询处理

    在Asp.net Web API中,对业务数据的分页查询处理是一个非常常见的接口,我们需要在查询条件对象中,定义好相应业务的查询参数,排序信息,请求记录数和每页大小信息等内容,根据这些查询信息,我们在 ...

最新文章

  1. Fragment里面嵌套Fragment注意事项
  2. 《kafka中文手册》- 构架设计(一)
  3. 大数据技术-hive窗口函数详解
  4. action怎么获得 ajax date参数_ajax()gt;load()事件的新用法!!!
  5. iframe里面的元素触发父窗口元素事件的jquery代码 转
  6. 推行CMMI能在哪些方面为软件企业带来好处?
  7. Bailian4145 放弃考试 POJ2976 ZOJ3068 Dropping tests【二分法+01分数规划】
  8. Java 多态的特性和概念
  9. java 发送信号_java – 在Windows中发送任意信号?
  10. 论文笔记_S2D.57_2018-IROS_LIMO:激光雷达单目视觉里程计
  11. 羞,Spring Bean 初始化/销毁竟然有这么多姿势
  12. 网课查课插件 支持 60类型+ 彩虹查课插件 网络查课查询
  13. 数学分析(9): 不定积分
  14. 在一夜暴富之前,我先一夜秃了头
  15. Nginx动静分离配置
  16. 蜻蜓FM回应恶意代码事件 音频行业仍将现721格局
  17. HTTP_REFERER的用法及伪造及去掉REFERER方法
  18. response.getWriter()
  19. linux系统登录新用户,Linux为新员工创建用户以及配置登录公钥
  20. Android webview支持H5页面通过js实现文件上传、图片上传

热门文章

  1. 【优化求解】基于matlab罚函数的粒子群算法函数寻优【含Matlab源码 216期】
  2. 涉密计算机多用户审批表,涉密计算机及信息系统安全策略
  3. android代码打包so,AndroidStudio jni开发入门及打包so库和jar包
  4. 例2.9 找x - 九度教程第17题(查找)
  5. 二进制,逆向工程,深入理解计算机系统
  6. linux查看命令源rpm,Linux rpm查询命令以及RPM包验证
  7. php中联合运算符,PHP-串联运算符
  8. 自己学java需要多久_自学 java, 学多久可以自己找到工作?
  9. nova.api.openstack.auth解析(ocata版本)
  10. Go常用功能总结一阶段