Open SuellaSun opened 6 years ago
AngularJS指令进阶 – ngModelController详解 在自定义Angular指令时,其中有一个叫做require的字段,这个字段的作用是用于指令之间的相互交流。举个简单的例子,假如我们现在需要编写两 个指令,在linking函数中有很多重合的方法,为了避免重复自己(著名的DRY原则),我们可以将这个重复的方法写在第三个指令的 controller中,然后在另外两个需要的指令中require这个拥有controller字段的指令,最后通过linking函数的第四个参数就 可以引用这些重合的方法。代码的结构大致如下: var app = angular.modeule('myapp',[]);
app.directive('common',function(){ return { ... controller: function($scope){ this.method1 = function(){ }; this.method2 = function(){ }; }, ... } });
app.directive('d1',function(){ return { ... require: '?^common', link: function(scope,elem,attrs,common){ scope.method1 = common.method1; .. }, ... } });
app.directive('d2',function(){ return { ... require: '?^common', link: function(scope,elem,attrs,common){ scope.method1 = common.method1; .. }, ... } }); 当然,上面例子只是指令中controller用法的一种。虽然一般来说,使用controller字段的机会不是很多,但是想要写好AngularJS的指令,这是必须要掌握的一点。 显然,controller的用法分为两种情形,一种是require自定义的controller,由于自定义controller中的属性方法都由自 己编写,使用起来比较简单;另一种方法则是require AngularJS内建的指令,其中大部分时间需要require的都是ngModel这个指令。很多时候,由于我们对ngModel中内建的方法和属性 不熟悉,在阅读和编写代码时会有一些困难。今天我们的目的就是详细的介绍ngModel中的内建属性和方法,相信在认真的阅读完本文之后,你一定能够熟练 的在指令中require ngModel。 在Angular应用中,ng-model指令时不可缺少的一个部分,它用来将视图绑定到数据,是双向绑定魔法中重要的一环。 ngModelController则是ng-model指令中所定义的controller。这个controller包含了一些用于数据绑定,验 证,CSS更新,以及数值格式化和解析的服务。它不用来进行DOM渲染或者监听DOM事件。与DOM相关的逻辑都应该包含在其他的指令中,然后让这些指令 来试用ngModelController中的数据绑定功能。 下面,我们将用一个例子来说明如何在自定义指令中require ngModel。在这里例子中,我们使用HTML5中的contenteditable属性来制作了一个简单的编辑器指令,同时将在指令定义中require了ngModel来进行数据绑定。 HTML部分
指令定义部分 angular.module('customControl', []). directive('contenteditable', function() { return { restrict: 'A', // 作为元素属性 require: '?ngModel', // 获取ngModelController link: function(scope, element, attrs, ngModel) { if(!ngModel) return; // 如果没有ng-model则什么都不做 // 指定UI的更新方式 ngModel.$render = function() { element.html(ngModel.$viewValue || ''); };
// 监听change事件来开启绑定
element.on('blur keyup change', function() {
scope.$apply(read);
});
read(); // 初始化
// 将数据写入model
function read() {
var html = element.html();
// 当我们清空div时浏览器会留下一个<br>标签
// 如果制定了strip-br属性,那么<br>标签会被清空
if( attrs.stripBr && html == '<br>' ) {
html = '';
}
ngModel.$setViewValue(html);
}
} }; }); ngModelController方法 $render(); 当视图需要更新的时候会被调用。使用ng-model的指令应该自行实现这个方法。 $isEmpty(value); 该方法用于判断输入值是否为空。 例如,使用ngModelController的指令需要判断其中是否有输入值的时候会使用该方法。该方法可用来判断值是否为undefined,'',null或者NaN。 你可以根据自己的需要重载该方法。 $setValidity(validationErrorKey, isValid); 该方法用于改变验证状态,以及在控制变化的验证标准时通知表格。 这个方法应该由一个验证器来调用。例如,一个解析器或者格式化函数。 $setPristine(); 该方法用于设置控制到原始状态。 该方法可以移除'ng-dirty'类并将控制恢复到原始状态('ng-pristine'类)。 $cancelUpdate(); 该方法用于取消一次更新并重置输入元素的值以防止$viewCalue发生更新,它会由一个pending debounced事件引发或者是因为input输入框要等待一些未来的事件。 如果你有一个使用了ng-model-options指令的输入框,并为它设置了debounced事件或者是类似于blur的事件,那么你可能会碰到在某一段时间内输入框中值和ngModel的$viewValue属性没有保持同步的情况。 在这种情况下,如果你试着在debounced/future事件发生之前更新ngModel的$modelValue,你很有可能遇到困难,因为AngularJS的dirty cheching机制实际上并不会分辨一个模型究竟有没有发生变化。 $cancelUpdate()方法应该在改变一个输入框的model之前被调用。记住,这很重要因为这能够确保输入字段能够被新的model值更新,而pending操作将会被取消。下面是一个例子: HTML部分
.controller('CancelUpdateCtrl', function($scope) {
$scope.resetWithCancel = function (e) {
if (e.keyCode == 27) {
$scope.myForm.myInput1.$cancelUpdate();
$scope.myValue = '';
}
};
$scope.resetWithoutCancel = function (e) {
if (e.keyCode == 27) {
$scope.myValue = '';
}
};
});
$setViewValue(value, trigger)方法
该方法用来更新视图值。这个方法应该在一个视图值发生变化时被调用,一般来说是在一个DOM事件处理函数中。例如,input和select指令就调用了这个函数。
这个方法将会更新$viewValue属性,然后在$pasers中通将这个值传递给每一个函数,其中包括了验证器。这个值从$parsers输出后,将会被用于$modelValue以及ng-model属性中的表达式。
最后,所有位于$viewChangeListeners列表中注册的监听器将会被调用。
ngModelController中的属性 $viewValue 视图中的实际值 $modelValue model中的值,它金额控制器绑定在一起 $parsers 将要执行的函数的数组,无论什么时候控制器从DOM中读取了一个值,它都将作为一个管道。其中的函数依次被调用,并将结果传递给下一个。最后出来的 值将会被传递到model中。其中将包括验证和转换值的过程。对于验证步骤,这个解析器将会使用$setValidity方法,对于不合格的值将返回 undefined。 $formatters 一个包含即将执行函数的数组,无论什么时候model的值发生了变化,它都会作为一个管道。其中的每一个函数都被依次调用,并将结果传递给下一个函数。该函数用于将模型传递给视图的值进行格式化。 $viewChangeListeners 只要视图的值发生变化,其中的函数就会被执行。其中的函数执行并不带参数,它的返回值也会被忽略。它可以被用在额外的#watches中。 $error 一个包含所有error的对象 $pristine 如果用户还没有进行过交互,值是true。 $dirty 如果用户已经进行过交互,值是true。 $valid 如果没有错误,值是true。 $invalid 如果有错误,值是true。
var http = require('http');
var url = require('url');
var path = require('path');
var static = require('./lib/stReqHandler');
var dynamic = require('./lib/dyReqHandler');
var PORT = 3000;
//添加MIME类型
var MIME_TYPE = {
"css": "text/css",
"gif": "image/gif",
"html": "text/html",
"ico": "image/x-icon",
"jpeg": "image/jpeg",
"jpg": "image/jpeg",
"js": "text/javascript",
"json": "application/json",
"pdf": "application/pdf",
"png": "image/png",
"svg": "image/svg+xml",
"swf": "application/x-shockwave-flash",
"tiff": "image/tiff",
"txt": "text/plain",
"wav": "audio/x-wav",
"wma": "audio/x-ms-wma",
"wmv": "video/x-ms-wmv",
"xml": "text/xml"
};
var server = http.createServer(onRequest);
//服务器监听程序
function onRequest(req, res) {
var pathname = url.parse(req.url).pathname;
//获取后缀名并解析为相应的文件类型
var suffix = path.extname(pathname);
//根据后缀名区分静态资源和动态资源请求
if (suffix != null && suffix.trim() != "") {
var contentType = MIME_TYPE[suffix.slice(1)] || "text/plain";
//static.staticReqHandler(pathname, contentType,suffix, res);
static.staticReqHandler(pathname, contentType,suffix, res);
} else {
dynamic.dynamicReqHandler(pathname, req, res);
}
}//onRequest
server.listen(PORT);
console.log("Server runing at port: " + PORT + ".");
fs = require('fs');
//渲染页面
function sendPage(pathname, contentType, res) {
fs.exists(pathname, function (err) {
if (!err) {
send404(res);
} else {
fs.readFile(pathname, function (err, data) {
if (err) {
send500(res);
} else {
res.writeHead(200, { 'content-type': contentType });
res.end(data.toString());
}
});
}
});//path.exists
}
function send404(res) {
res.writeHead(404, { 'content-type': 'text/html; charset = utf-8' });
res.end("<h1>404</h1><p>file not found</p>");
}
function send500(res) {
res.writeHead(500, { 'content-type': 'text/html; charset = utf-8' });
res.end("<h1>404</h1><p>server error</p>");
}
exports.sendPage = sendPage;
exports.send404 = send404;
exports.send500 = send500;
var fs = require('fs');
var send = require('./sendPages');
//静态资源处理
function staticReqHandler(pathname, contentType, suffix, res) {
//由于所有的html文件放在views文件夹下,页面加载css等非html文件时,
//请求的css等文件都会加上该html页面所在的父目录也即views,所以要
// // 将这个父目录去掉,后面的目录才是文件所在的目录
console.log(pathname);
// if (pathname.slice(1, 7) === 'public' || pathname === "/index.js") {
// pathname = '.' + pathname;
// }
// else {
pathname = './public' + pathname;
// }
console.log(pathname);
send.sendPage(pathname, contentType, res);
}//staticReqHandler
exports.staticReqHandler = staticReqHandler;
send = require('./sendPages');
fs = require('fs');
//动态请求处理
function dynamicReqHandler(pathname, req, res) {
switch (pathname) {
case "/":
pathname = "./public/views/index.html";
send.sendPage(pathname, 'text/html', res);
break;
// case "/login":
// login(req,res);
case "/login":
login(req, res);
break;
case "/getBlogList":
getList(req, res);
break;
default:
res.writeHead(500, { 'content-type': 'text/html; charset = utf-8' });
res.end("<h1>500</h1><p>file not found</p>");
}//switch
}
// function login(req,res){
// console.log("request handler 'login' was called.");
// pathname = './public/views/login.html';
// send.sendPage(pathname, 'text/html', res);
// }
//用户登录验证
function login(req, res) {
console.log("Request handler 'login' was called.");
//处理post请求
// 定义了一个post变量,用于暂存请求体的信息
var postData = '';
// 通过req的data事件监听函数,每当接受到请求体的数据,就累加到postData变量中
req.on('data', function (chunk) {
postData += chunk;
});
// 在end事件触发后然后向客户端返回。
req.on('end', function () {
postData = JSON.parse(postData);//解析接收的浏览器请求
console.log(postData);
//返回给客户端的状态数据
var reslut = {};
if (postData.username == "2910776796@qq.com" && postData.password == "12345678") {
reslut = {
'code': 1,
'msg': 'sucess'
};
console.log(reslut);
} else {
reslut = {
'code': 0,
'msg': 'false'
};
console.log(reslut);
}
res.writeHead(200, { 'content-Type': 'text/plain' });
res.end(JSON.stringify(reslut));
});
}
//读取JSON数据
function getList(req, res) {
console.log("Request handler 'getList' was called.");
fs.readFile('./public/json/blogItem.json', 'utf-8', function (err, blogData) {
if (err)
send.send404(res);
res.writeHead(200, { 'content-type': 'application/json;charset=utf-8;' });
res.end(blogData);
});
return;
}
exports.dynamicReqHandler = dynamicReqHandler;
var app = angular.module('myApp', ['ngRoute']);
// 路由控制
app.config(['$routeProvider', function ($routeProvider) {
$routeProvider
.when('/login',{
controller: 'loginCrtl',
templateUrl: '../views/login.html'
})
.when('/bloghome', {
controller: 'blogListCtrl',
templateUrl: '../views/blogHome.html'
})
.otherwise({
redirectTo: '/'
});
}]);
// 主页控制器
app.controller("homeCtrl", ['$scope','$http', '$location', function ($scope, $http, $location) {
$scope.login = function () {
$location.path('/login').replace();
}
}]);
// 登录页控制器
app.controller("loginCrtl", ['$scope','$http','$location', function ($scope,$http,$location) {
$scope.isLogout = true;
$scope.isLogin = false;
$scope.dataSumbit = function () {
if ($scope.myForm.$valid) {
$http({
method: 'post',
url: '/login',
data: $scope.user
}).then(function (res) {
if (res.data.code == 1) {
$scope.isLogout = false;
$scope.isLogin = true;
$location.path('/bloghome');
}
else {
alert('用户名或密码错误!请重新输入');
$location.path('/login').replace();
}
});
}
}
}]);
// 博客列表页控制器
app.controller('blogListCtrl', ['$scope', '$http', function ( $scope, $http) {
$http({//与后端交互,获取数据
method: 'GET',
url: '/getBlogList'
}).then(function (res) {
if (res.status !== 200)
alert(res.status + "状态,出错啦!");
$scope.heads = res.data.bloghead;
$scope.blogItems = res.data.blogitem;
});
//利用事件委托实现事件绑定一次
var myUL = document.getElementById("blogList");
var myLI = myUL.getElementsByClassName("listItem");
$scope.blogClickHandler = function ($event) {
var event = $event || window.event;
var target = event.target || event.srcElement;// 兼容IE和Firefox
target = getParentByClassName(target, "listItem");
if (target.classList.contains("listItem")) {
for (var i = 0; i < myLI.length; i++) {
if (myLI[i] === target) {
var children = target.children;
alert(children[1].innerHTML);// 这里目前用的还是DOM
}
}
}
}
$scope.changeOver = function ($event) {
var event = $event || window.event;
var target = event.target || event.srcElement;//兼容IE和Firefox
target = getParentByClassName(target, "listItem");
if (target.classList.contains("listItem")) {
for (var i = 0; i < myLI.length; i++) {
if (myLI[i] === target) {
target.style.backgroundColor = "WhiteSmoke";// 这里目前用的还是DOM
}
}
}
}
$scope.changeOut = function ($event) {
var event = $event || window.event;
var target = event.target || event.srcElement;//兼容IE和Firefox
target = getParentByClassName(target, "listItem");
if (target.classList.contains("listItem")) {
for (var i = 0; i < myLI.length; i++) {
if (myLI[i] === target) {
target.style.backgroundColor = "";// 这里目前用的还是DOM
}
}
}
}
}]);
// 自定义验证用户名的指令
app.directive("usernameValidate", function () {
return {
restrict: 'A',
require: 'ngModel',
link: function (scope, elem, attrs, ctrl) {
var uNameRegexp = /^(?![0-9]+$)[a-zA-Z_][a-zA-Z_0-9]{8,20}$/;//不能以数字开头,可包含数字字母下划线,长度6-20位
var phoneRegexp = /^1[3|5|8][0-9]\d{4,8}$/;//
var emailRegexp = /^([a-z0-9]*[-_.]?[a-z0-9]+)+@([a-z0-9]*[-_]?[a-z0-9]+)+[\.][a-z]{2,3}([\.][a-z]{2})?$/;
var isPhone = /^\d{11,11}$/;//11位的纯数字
var isEmail = /[\@]/;//包含@
ctrl.$parsers.unshift(function (viewValue) {
if (isPhone.test(viewValue)) {//纯数字
if (phoneRegexp.test(viewValue))
ctrl.$setValidity('formatValid', true);
else
ctrl.$setValidity('formatValid', false);
} else if (isEmail.test(viewValue)) {
if (emailRegexp.test(viewValue))
ctrl.$setValidity('formatValid', true);
else
ctrl.$setValidity('formatValid', false);
} else {
if (uNameRegexp.test(viewValue))
ctrl.$setValidity('formatValid', true);
else
ctrl.$setValidity('formatValid', false);
}
return viewValue;
});
ctrl.$render = function () { };
}
}
});
// 判断祖先元素是否为指定元素(一级级向上查找,直到找到或者到body元素)
function getParentByClassName(child, parentClass) {
var parent;
// 如果当前元素为要找的元素,则返回当前元素
// if (child.className === parentClass)//修改前
if (child.classList.contains(parentClass))//修改后获取所有类名列表,查看列表中是否包含
parent = child;
else {// 否则,查看其父元素是否为要找的元素,一直向上查询直到找到或者到达body,也就是没找到
while (!(child.parentNode.classList.contains(parentClass)) && child.parentNode.tagName != "body") {
child = child.parentNode;
}
parent = child.parentNode;
}
return parent;
}
<div class="wrapper1">
<form name="myForm" class="parent" ng-submit="dataSumbit()" novalidate>
<div class="login-header">
<h3>用户登录</h3>
</div>
<input class="child" type="text" name="userName" placeholder="用户名/手机号/邮箱" ng-model="user.username" username-Validate required>
</input>
<span ng-show="myForm.userName.$dirty && myForm.userName.$touched">
<span class="showError" ng-show="myForm.userName.$error.required">用户名不能为空</span>
<span class="showError" ng-show="myForm.userName.$error.formatValid">用户名格式错误。</span>
</span>
<br>
<input class="child" type="password" name="password" placeholder="密码" ng-model="user.password" required>
</input>
<span class="showError" style="color:red" ng-show="myForm.password.$dirty && myForm.password.$touched">
<span class="showError" ng-show="myForm.password.$error.required">密码不能为空</span>
</span>
<br>
<input id="chk" type="checkbox" name="autologin" value="no">
<span class="chbx">下一次自动登录</span>
<br>
<button type="submit" class="child btnlogin" name="login">登录</button>
<br>
</form>
</div>
HTTP 400 - 请求无效 HTTP 401.1 - 未授权:登录失败 HTTP 401.2 - 未授权:服务器配置问题导致登录失败 HTTP 401.3 - ACL 禁止访问资源 HTTP 401.4 - 未授权:授权被筛选器拒绝 HTTP 401.5 - 未授权:ISAPI 或 CGI 授权失败 HTTP 403 - 禁止访问 HTTP 403 - 对 Internet 服务管理器 的访问仅限于 Localhost HTTP 403.1 禁止访问:禁止可执行访问 HTTP 403.2 - 禁止访问:禁止读访问 HTTP 403.3 - 禁止访问:禁止写访问 HTTP 403.4 - 禁止访问:要求 SSL HTTP 403.5 - 禁止访问:要求 SSL 128 HTTP 403.6 - 禁止访问:IP 地址被拒绝 HTTP 403.7 - 禁止访问:要求客户证书 HTTP 403.8 - 禁止访问:禁止站点访问 HTTP 403.9 - 禁止访问:连接的用户过多 HTTP 403.10 - 禁止访问:配置无效 HTTP 403.11 - 禁止访问:密码更改 HTTP 403.12 - 禁止访问:映射器拒绝访问 HTTP 403.13 - 禁止访问:客户证书已被吊销 HTTP 403.15 - 禁止访问:客户访问许可过多 HTTP 403.16 - 禁止访问:客户证书不可信或者无效 HTTP 403.17 - 禁止访问:客户证书已经到期或者尚未生效 HTTP 404.1 - 无法找到 Web 站点 HTTP 404- 无法找到文件 HTTP 405 - 资源被禁止 HTTP 406 - 无法接受 HTTP 407 - 要求代理身份验证 HTTP 410 - 永远不可用 HTTP 412 - 先决条件失败 HTTP 414 - 请求 - URI 太长 HTTP 500 - 内部服务器错误 HTTP 500.100 - 内部服务器错误 - ASP 错误 HTTP 500-11 服务器关闭 HTTP 500-12 应用程序重新启动 HTTP 500-13 - 服务器太忙 HTTP 500-14 - 应用程序无效 HTTP 500-15 - 不允许请求 global.asa Error 501 - 未实现 HTTP 502 - 网关错误 用户试图通过 HTTP 或文件传输协议 (FTP) 访问一台正在运行 Internet 信息服务 (IIS) 的服务器上的内容时,IIS 返回一个表示该请求的状态的数字代码。该状态代码记录在 IIS 日志中,同时也可能在 Web 浏览器或 FTP 客户端显示。状态代码可以指明具体请求是否已成功,还可以揭示请求失败的确切原因。 日志文件的位置 在默认状态下,IIS 把它的日志文件放在 %WINDIRSystem32Logfiles 文件夹中。每个万维网 (WWW) 站点和 FTP 站点在该目录下都有一个单独的目录。在默认状态下,每天都会在这些目录下创建日志文件,并用日期给日志文件命名(例如,exYYMMDD.log)。 HTTP 1xx - 信息提示 这些状态代码表示临时的响应。客户端在收到常规响应之前,应准备接收一个或多个 1xx 响应。 • 100 - 继续。 • 101 - 切换协议。 2xx - 成功 这类状态代码表明服务器成功地接受了客户端请求。 • 200 - 确定。客户端请求已成功。 • 201 - 已创建。 • 202 - 已接受。 • 203 - 非权威性信息。 • 204 - 无内容。 • 205 - 重置内容。 • 206 - 部分内容。 3xx - 重定向 客户端浏览器必须采取更多操作来实现请求。例如,浏览器可能不得不请求服务器上的不同的页面,或通过代理服务器重复该请求。 • 302 - 对象已移动。 • 304 - 未修改。 • 307 - 临时重定向。 4xx - 客户端错误 发生错误,客户端似乎有问题。例如,客户端请求不存在的页面,客户端未提供有效的身份验证信息。 • 400 - 错误的请求。 • 401 - 访问被拒绝。IIS 定义了许多不同的 401 错误,它们指明更为具体的错误原因。这些具体的错误代码在浏览器中显示,但不在 IIS 日志中显示: • 401.1 - 登录失败。 • 401.2 - 服务器配置导致登录失败。 • 401.3 - 由于 ACL 对资源的限制而未获得授权。 • 401.4 - 筛选器授权失败。 • 401.5 - ISAPI/CGI 应用程序授权失败。 • 401.7 – 访问被 Web 服务器上的 URL 授权策略拒绝。这个错误代码为 IIS 6.0 所专用。 • 403 - 禁止访问:IIS 定义了许多不同的 403 错误,它们指明更为具体的错误原因: • 403.1 - 执行访问被禁止。 • 403.2 - 读访问被禁止。 • 403.3 - 写访问被禁止。 • 403.4 - 要求 SSL。 • 403.5 - 要求 SSL 128。 • 403.6 - IP 地址被拒绝。 • 403.7 - 要求客户端证书。 • 403.8 - 站点访问被拒绝。 • 403.9 - 用户数过多。 • 403.10 - 配置无效。 • 403.11 - 密码更改。 • 403.12 - 拒绝访问映射表。 • 403.13 - 客户端证书被吊销。 • 403.14 - 拒绝目录列表。 • 403.15 - 超出客户端访问许可。 • 403.16 - 客户端证书不受信任或无效。 • 403.17 - 客户端证书已过期或尚未生效。 • 403.18 - 在当前的应用程序池中不能执行所请求的 URL。这个错误代码为 IIS 6.0 所专用。 • 403.19 - 不能为这个应用程序池中的客户端执行 CGI。这个错误代码为 IIS 6.0 所专用。 • 403.20 - Passport 登录失败。这个错误代码为 IIS 6.0 所专用。 • 404 - 未找到。 • 404.0 -(无) – 没有找到文件或目录。 • 404.1 - 无法在所请求的端口上访问 Web 站点。 • 404.2 - Web 服务扩展锁定策略阻止本请求。 • 404.3 - MIME 映射策略阻止本请求。 • 405 - 用来访问本页面的 HTTP 谓词不被允许(方法不被允许) • 406 - 客户端浏览器不接受所请求页面的 MIME 类型。 • 407 - 要求进行代理身份验证。 • 412 - 前提条件失败。 • 413 – 请求实体太大。 • 414 - 请求 URI 太长。 • 415 – 不支持的媒体类型。 • 416 – 所请求的范围无法满足。 • 417 – 执行失败。 • 423 – 锁定的错误。 5xx - 服务器错误 服务器由于遇到错误而不能完成该请求。 • 500 - 内部服务器错误。 • 500.12 - 应用程序正忙于在 Web 服务器上重新启动。 • 500.13 - Web 服务器太忙。 • 500.15 - 不允许直接请求 Global.asa。 • 500.16 – UNC 授权凭据不正确。这个错误代码为 IIS 6.0 所专用。 • 500.18 – URL 授权存储不能打开。这个错误代码为 IIS 6.0 所专用。 • 500.100 - 内部 ASP 错误。 • 501 - 页眉值指定了未实现的配置。 • 502 - Web 服务器用作网关或代理服务器时收到了无效响应。 • 502.1 - CGI 应用程序超时。 • 502.2 - CGI 应用程序出错。application. • 503 - 服务不可用。这个错误代码为 IIS 6.0 所专用。 • 504 - 网关超时。 • 505 - HTTP 版本不受支持。 常见的 HTTP 状态代码及其原因 • 200 - 成功。 此状态代码表示 IIS 已成功处理请求。 • 304 - 未修改。客户端请求的文档已在其缓存中,文档自缓存以来尚未被修改过。客户端使用文档的缓存副本,而不从服务器下载文档。 • 401.1 - 登录失败。 登录尝试不成功,可能因为用户名或密码无效。 • 401.3 - 由于 ACL 对资源的限制而未获得授权。 这表示存在 NTFS 权限问题。即使您对试图访问的文件具备相应的权限,也可能发生此错误。例如,如果 IUSR 帐户无权访问 C:WinntSystem32Inetsrv 目录,您会看到这个错误。 有关如何解决此问题的其他信息,请单击下面的文章编号,查看 Microsoft 知识库中相应的文章: 187506 INFO: IIS 4.0 的基础 NTFS 权限 • 403.1 - 执行访问被禁止。 下面是导致此错误信息的两个常见原因: • 您没有足够的执行许可。例如,如果试图访问的 ASP 页所在的目录权限设为“无”,或者,试图执行的 CGI 脚本所在的目录权限为“只允许脚本”,将出现此错误信息。若要修改执行权限,请在 Microsoft 管理控制台 (MMC) 中右击目录,然后依次单击属性和目录选项卡,确保为试图访问的内容设置适当的执行权限。 • 您没有将试图执行的文件类型的脚本映射设置为识别所使用的谓词(例如,GET 或 POST)。若要验证这一点,请在 MMC 中右击目录,依次单击属性、目录选项卡和配置,然后验证相应文件类型的脚本映射是否设置为允许所使用的谓词。 • 403.2 - 读访问被禁止。验证是否已将 IIS 设置为允许对目录进行读访问。另外,如果您正在使用默认文件,请验证该文件是否存在。有关如何解决此问题的其他信息,请单击下面的文章编号,查看 Microsoft 知识库中相应的文章: 247677 错误信息:403.2 Forbidden:Read Access Forbidden(403.2 禁止访问:读访问被禁止) • 403.3 - 写访问被禁止。 验证 IIS 权限和 NTFS 权限是否已设置以便向该目录授予写访问权。有关如何解决此问题的其他信息,请单击下面的文章编号,查看 Microsoft 知识库中相应的文章: 248072 错误信息:403.3 Forbidden:Write Access Forbidden(403.3 禁止访问:写访问被禁止) • 403.4 - 要求 SSL。禁用要求安全通道选项,或使用 HTTPS 代替 HTTP 来访问该页面。如果没有安装证书的 Web 站点出现此错误,请单击下面的文章编号,查看 Microsoft 知识库中相应的文章: 224389 错误信息:HTTP 错误 403、403.4、403.5 禁止访问:要求 SSL • 403.5 - 要求 SSL 128。禁用要求 128 位加密选项,或使用支持 128 位加密的浏览器以查看该页面。如果没有安装证书的 Web 站点出现此错误,请单击下面的文章编号,查看 Microsoft 知识库中相应的文章: 224389 错误信息:HTTP 错误 403、403.4、403.5 禁止访问:要求 SSL • 403.6 - IP 地址被拒绝。您已把您的服务器配置为拒绝访问您目前的 IP 地址。有关如何解决此问题的其他信息,请单击下面的文章编号,查看 Microsoft 知识库中相应的文章: 248043 错误信息:403.6 - Forbidden:IP Address Rejected(403.6 - 不可用:IP 地址被拒绝) • 403.7 - 要求客户端证书。您已把您的服务器配置为要求客户端身份验证证书,但您未安装有效的客户端证书。有关其他信息,请单击下面的文章编号,查看 Microsoft 知识库中相应的文章: 190004 错误 403.7 或“Connection to Server Could Not Be Established”(无法建立与服务器的连接) 186812 PRB:错误信息:403.7 Forbidden:Client Certificate Required(403.7 禁止访问:要求客户端证书) • 403.8 - 站点访问被拒绝。您已为您用来访问服务器的域设置了域名限制。有关如何解决此问题的其他信息,请单击下面的文章编号,查看 Microsoft 知识库中相应的文章: 248032 错误信息:Forbidden:Site Access Denied 403.8(禁止访问:站点访问被拒绝 403.8) • 403.9 - 用户数过多。与该服务器连接的用户数量超过了您设置的连接限制。有关如何更改此限制的其他信息,请单击下面的文章编号,以查看 Microsoft 知识库中相应的文章: 248074 错误信息:Access Forbidden:Too Many Users Are Connected 403.9(禁止访问:连接的用户太多 403.9) 注意:Microsoft Windows 2000 Professional 和 Microsoft Windows XP Professional 自动设置了在 IIS 上最多 10 个连接的限制。您无法更改此限制。 • 403.12 - 拒绝访问映射表。 您要访问的页面要求提供客户端证书,但映射到您的客户端证书的用户 ID 已被拒绝访问该文件。有关其他信息,请单击下面的文章编号,以查看 Microsoft 知识库中相应的文章: 248075 错误信息:HTTP 403.12 - Access Forbidden:Mapper Denied Access(HTTP 403.12 - 禁止访问:映射表拒绝访问) • 404 - 未找到。 发生此错误的原因是您试图访问的文件已被移走或删除。如果在安装 URLScan 工具之后,试图访问带有有限扩展名的文件,也会发生此错误。这种情况下,该请求的日志文件项中将出现“Rejected by URLScan”的字样。 • 500 - 内部服务器错误。 很多服务器端的错误都可能导致该错误信息。事件查看器日志包含更详细的错误原因。此外,您可以禁用友好 HTTP 错误信息以便收到详细的错误说明。 有关如何禁用友好 HTTP 错误信息的其他信息,请单击下面的文章编号,以查看 Microsoft 知识库中相应的文章: 294807 如何在服务器端禁用 Internet Explorer 5 的“显示友好 HTTP 错误信息”功能 • 500.12 - 应用程序正在重新启动。 这表示您在 IIS 重新启动应用程序的过程中试图加载 ASP 页。刷新页面后,此信息即会消失。如果刷新页面后,此信息再次出现,可能是防病毒软件正在扫描 Global.asa 文件。有关其他信息,请单击下面的文章编号,以查看 Microsoft 知识库中相应的文章: 248013 错误信息:HTTP Error 500-12 Application Restarting(HTTP 错误 500-12 应用程序正在重新启动) • 500-100.ASP - ASP 错误。 如果试图加载的 ASP 页中含有错误代码,将出现此错误信息。若要获得更确切的错误信息,请禁用友好 HTTP 错误信息。默认情况下,只会在默认 Web 站点上启用此错误信息。有关如何在非默认的 Web 站点上看到此错误信息的其他信息,请单击下面的文章编号,以查看 Microsoft 知识库中相应的文章: 261200 显示 HTTP 500 错误信息,而不显示 500-100.asp 的 ASP 错误信息 • 502 - 网关错误。 如果试图运行的 CGI 脚本不返回有效的 HTTP 标头集,将出现此错误信息。 FTP 1xx - 肯定的初步答复 这些状态代码指示一项操作已经成功开始,但客户端希望在继续操作新命令前得到另一个答复。 • 110 重新启动标记答复。 • 120 服务已就绪,在 nnn 分钟后开始。 • 125 数据连接已打开,正在开始传输。 • 150 文件状态正常,准备打开数据连接。 2xx - 肯定的完成答复 一项操作已经成功完成。客户端可以执行新命令。 • 200 命令确定。 • 202 未执行命令,站点上的命令过多。 • 211 系统状态,或系统帮助答复。 • 212 目录状态。 • 213 文件状态。 • 214 帮助消息。 • 215 NAME 系统类型,其中,NAME 是 Assigned Numbers 文档中所列的正式系统名称。 • 220 服务就绪,可以执行新用户的请求。 • 221 服务关闭控制连接。如果适当,请注销。 • 225 数据连接打开,没有进行中的传输。 • 226 关闭数据连接。请求的文件操作已成功(例如,传输文件或放弃文件)。 • 227 进入被动模式 (h1,h2,h3,h4,p1,p2)。 • 230 用户已登录,继续进行。 • 250 请求的文件操作正确,已完成。 • 257 已创建“PATHNAME”。 3xx - 肯定的中间答复 该命令已成功,但服务器需要更多来自客户端的信息以完成对请求的处理。 • 331 用户名正确,需要密码。 • 332 需要登录帐户。 • 350 请求的文件操作正在等待进一步的信息。 4xx - 瞬态否定的完成答复 该命令不成功,但错误是暂时的。如果客户端重试命令,可能会执行成功。 • 421 服务不可用,正在关闭控制连接。如果服务确定它必须关闭,将向任何命令发送这一应答。 • 425 无法打开数据连接。 • 426 Connection closed; transfer aborted. • 450 未执行请求的文件操作。文件不可用(例如,文件繁忙)。 • 451 请求的操作异常终止:正在处理本地错误。 • 452 未执行请求的操作。系统存储空间不够。 5xx - 永久性否定的完成答复 该命令不成功,错误是永久性的。如果客户端重试命令,将再次出现同样的错误。 • 500 语法错误,命令无法识别。这可能包括诸如命令行太长之类的错误。 • 501 在参数中有语法错误。 • 502 未执行命令。 • 503 错误的命令序列。 • 504 未执行该参数的命令。 • 530 未登录。 • 532 存储文件需要帐户。 • 550 未执行请求的操作。文件不可用(例如,未找到文件,没有访问权限)。 • 551 请求的操作异常终止:未知的页面类型。 • 552 请求的文件操作异常终止:超出存储分配(对于当前目录或数据集)。 • 553 未执行请求的操作。不允许的文件名。 常见的 FTP 状态代码及其原因 • 150 - FTP 使用两个端口:21 用于发送命令,20 用于发送数据。状态代码 150 表示服务器准备在端口 20 上打开新连接,发送一些数据。 • 226 - 命令在端口 20 上打开数据连接以执行操作,如传输文件。该操作成功完成,数据连接已关闭。 • 230 - 客户端发送正确的密码后,显示该状态代码。它表示用户已成功登录。 • 331 - 客户端发送用户名后,显示该状态代码。无论所提供的用户名是否为系统中的有效帐户,都将显示该状态代码。 • 426 - 命令打开数据连接以执行操作,但该操作已被取消,数据连接已关闭。 • 530 - 该状态代码表示用户无法登录,因为用户名和密码组合无效。如果使用某个用户帐户登录,可能键入错误的用户名或密码,也可能选择只允许匿名访问。如果使用匿名帐户登录,IIS 的配置可能拒绝匿名访问。 • 550 - 命令未被执行,因为指定的文件不可用。例如,要 GET 的文件并不存在,或试图将文件 PUT 到您没有写入权限的目录。
dy.js
var send = require('./send-page');
var fs = require('fs');
var write = require('./write-data');
var path = require('path');
//动态请求处理
function dynamicReqHandler(pathname, req, res) {
switch (pathname) {
case "/":
pathname = "./public/views/index.html";
send.sendPage(pathname, 'text/html', res);
break;
case "/login":
login(req, res);
break;
case "/signup":
signup(req, res);
break;
case "/uniqueValidate":
uniqueValidate(req, res);
break;
case "/postBlogs":
postBlog(req, res);
break;
case "/getBlogList":
getList(res);
break;
case "/getPersonalhome":
getPersonalHomePage(req, res);
break;
case "/getArticleDetails":
getArticleDetails(req, res);
break;
default:
send.send500(res);
}//switch
}
// 用户登录验证
function login(req, res) {
console.log("Request handler 'login' was called.");
var postData = '';
// 通过req的data事件监听函数,每当接受到请求体的数据,就累加到postData变量中
req.on('data', function (chunk) {
postData += chunk;
});
// 在end事件触发后然后向客户端返回。
req.on('end', function () {
postData = JSON.parse(postData);// 解析接收的浏览器请求
console.log(postData);
// 返回给客户端的状态数据
var reslut = { 'status': '' };
fs.readFile('./public/json/user-infomation.json', function (err, userInfo) {
if (err)
send.send500(res);
else {
userInfo = JSON.parse(userInfo);
var flag = userExists(userInfo, postData);
if (flag >= 0) {
reslut.status = 200;
}
else {
reslut.status = 401.1;
}
res.writeHead(200, { 'content-Type': 'text/plain' });
res.end(JSON.stringify(reslut));
}
});
});
}
// 注册
function signup(req, res) {
console.log("Request handler 'signup' was called.");
pathname = './public/json/user-infomation.json';
write.writeData(pathname, req, res);
}
// 验证用户名是否唯一
function uniqueValidate(req, res) {
console.log("Request handler 'uniqueValidate' was called.");
var postData = '';
// 通过req的data事件监听函数,每当接受到请求体的数据,就累加到postData变量中
req.on('data', function (chunk) {
postData += chunk;
});
// 在end事件触发后然后向客户端返回。
req.on('end', function () {
postData = JSON.parse(postData);// 解析接收的浏览器请求
console.log(postData);
// 返回给客户端的状态数据
var reslut = { 'isUnique': '' };
fs.readFile('./public/json/user-infomation.json', function (err, userInfo) {
if (err)
send.send500(res);
else {
userInfo = JSON.parse(userInfo);
var flag = false;
for (var i = 0; i < userInfo.length; i++) {
if (userInfo[i].username === postData.username) {
flag = true;
}
}
if (flag) { // 存在,不唯一
reslut.isUnique = false;
}
else { // 不存在,唯一
reslut.isUnique = true;
}
res.writeHead(200, { 'content-Type': 'text/plain' });
res.end(JSON.stringify(reslut));
}
});
});
}
// 提交博客数据
function postBlog(req, res) {
console.log("Request handler 'postBlogs' was called.");
var postData = '';
req.on('data', function (chunk) {
postData += chunk;
});
req.on('end', function () {
postData = JSON.parse(postData);
fs.readFile('./public/json/user-infomation.json', function (err, userInfo) {
if (err)
send.send500(res);
else {
var pathname = getPath(userInfo, postData.user);
postData = {
"title": postData.blog.title,
"content": postData.blog.content,
"date": postData.blog.date,
"username": postData.user.username
}
postData = JSON.stringify(postData);
write.insertData1(pathname, postData, res);
}
});
});
}
// 首页读取JSON数据,显示博客列表(修改,显示所有用户的最新10条)
function getList(res) {
console.log("Request handler 'getList' was called.");
dirname = path.join(__dirname, '/../public/json/blog-information/');
let fileNames = findSync(dirname);
var datas = [];
// 获取/blog-information/下的所有json文件
for (let i = 0; i < fileNames.length; i++) {
datas[i] = fs.readFileSync(fileNames[i], 'utf-8');
}
// 获取非空文件列表
var copyDatas = getNotNullFile(datas);
// 读取所有非空文件中的数据(注意格式)
var blogData = "";
if (copyDatas.length != 0) {
var blogData = copyDatas[0].slice(0, copyDatas[0].length - 1);
for (var i = 1; i < copyDatas.length; i++) {
blogData += ',' + copyDatas[i].slice(1, copyDatas[i].length - 1);
}
blogData += ']';
// 以date降序重新排列blogData
blogData = JSON.parse(blogData);
blogData = blogData.sort(keySort("date"));
res.writeHead(200, { 'content-Type': 'application/json' });
res.end(JSON.stringify(blogData));
}
else {
// 空json数组
blogData = "[]";
res.writeHead(200, { 'content-Type': 'application/json' });
res.end(blogData);
}
}
// 读取个人主页数据
function getPersonalHomePage(req, res) {
console.log("Request handler 'getPersonalHomePage' was called.");
var postData = '';
req.on('data', function (chunk) {
postData += chunk;
});
// 在end事件触发后然后向客户端返回。
req.on('end', function () {
postData = JSON.parse(postData);// 解析接收的浏览器请求
// 返回给客户端的状态数据
var reslut = { 'status': '' };
fs.readFile('./public/json/user-infomation.json', function (err, userInfo) {
if (err)
send.send500(res);
else {
var pathname = getPath(userInfo, postData);
send.sendPage(pathname, "application/json", res);
}
});
});
}
// 获取博客详情
function getArticleDetails(req, res) {
console.log("Request handler 'getArticleDetails' was called.");
var postData = '';
req.on('data', function (chunk) {
postData += chunk;
});
// 在end事件触发后然后向客户端返回。
req.on('end', function () {
postData = JSON.parse(postData);// 解析接收的浏览器请求'
pathname = './public/json/blog-information/' + postData.username + '.json';
data = JSON.parse(fs.readFileSync(pathname, 'utf-8'));
// 获取对应id的博客
var resData = data[data.length-postData.id];
res.writeHead(200, { 'content-type': "application/json" });
res.end(JSON.stringify(resData));
});
}
// 获取非空文件列表
function getNotNullFile(arr) {
var copyArr = [];
for (let i = 0, j = 0; i < arr.length; i++) {
if (arr[i] !== "") {
copyArr[j++] = arr[i];
}
}
return copyArr;
}
// 根据JSON对象的属性值降序排序
function keySort(property){
return function(obj1,obj2){
var value1 = obj1[property];
var value2 = obj2[property];
// 降序 return 1,降序 return-1
return value2 - value1;
}
}
// 查看用户是否存在
function userExists(userInfo, postData) {
var flag = -1;
for (var i = 0; i < userInfo.length; i++) {
if (userInfo[i].username === postData.username && userInfo[i].password === postData.password) {
flag = i;
}
}
return flag;
}
// 获取对应用户的存储路径
function getPath(userInfo, postData) {
userInfo = JSON.parse(userInfo);
var flag = userExists(userInfo, postData);
if (flag >= 0) {
console.log(userInfo);
pathname = './public/json/blog-information/' + userInfo[flag].username + '.json';
}
return pathname;
}
// 获取指定目录下的文件名
function findSync(startPath) {
let result = [];
function finder(path) {
let files = fs.readdirSync(path);
files.forEach((val, index) => {
let fPath = path + val;
let stats = fs.statSync(fPath);
if (stats.isDirectory()) finder(fPath);
if (stats.isFile()) result.push(fPath);
});
}
finder(startPath);
return result;
}
exports.dynamicReqHandler = dynamicReqHandler;
mine
// 获取资源类型
var MIME_TYPE = {
"css": "text/css",
"gif": "image/gif",
"html": "text/html",
"ico": "image/x-icon",
"jpeg": "image/jpeg",
"jpg": "image/jpeg",
"js": "text/javascript",
"json": "application/json",
"pdf": "application/pdf",
"png": "image/png",
"svg": "image/svg+xml",
"swf": "application/x-shockwave-flash",
"tiff": "image/tiff",
"txt": "text/plain",
"wav": "audio/x-wav",
"wma": "audio/x-ms-wma",
"wmv": "video/x-ms-wmv",
"xml": "text/xml"
};
function getContentType(suffix) {
var contentType = MIME_TYPE[suffix.slice(1)] || "text/plain";
return contentType;
}
exports.getContentType = getContentType;
send.js
fs = require('fs');
//渲染页面
function sendPage(pathname, contentType, res) {
fs.exists(pathname, function (err) {
if (!err) {
send404(res);
} else {
fs.readFile(pathname, function (err, data) {
if (err) {
send500(res);
} else {
res.writeHead(200, { 'content-type': contentType });
res.end(data.toString());
}
});
}
});//path.exists
}
function send404(res) {
res.writeHead(404, { 'content-type': 'text/html; charset = utf-8' });
res.end("<h1>404</h1><p>file not found</p>");
}
function send500(res) {
res.writeHead(500, { 'content-type': 'text/html; charset = utf-8' });
res.end("<h1>500</h1><p>server error</p>");
}
exports.sendPage = sendPage;
exports.send404 = send404;
exports.send500 = send500;
static
var fs = require('fs');
var send = require('./send-page');
//静态资源处理
function staticReqHandler(pathname, contentType, res) {
console.log("receive:" + pathname);
pathname = './public' + pathname;
console.log("realpath:" + pathname);
send.sendPage(pathname, contentType, res);
}//staticReqHandler
exports.staticReqHandler = staticReqHandler;
write
var fs = require('fs');
var path = require('path');
var send = require('./send-page');
function writeData(pathname, req, res) {
var postData = '';
req.on('data', function (chunk) {
postData += chunk;
});
req.on('end', function () {
insertData(pathname, postData, res)
});
}
function insertData(pathname, postData, res) {
var reslut = { "status": "" };
var username = JSON.parse(postData).username;
// 查看源文件中是否有内容
fs.readFile(pathname, function (err, data) {
if (err)
send.send500(res);
else {
if (data == "") { // 如果文件为空,则写入格式如下
postData = '[' + postData + ']';
}
else { // 如果文件不为空,则在将待写入的数据加在最后一个元素后面
var buf = new Buffer(data);
postData = buf.slice(0, buf.length - 1) + ',' + postData + ']';
}
fs.writeFile(pathname, postData, 'utf8', function (err) {
if (err) {
console.log('保存失败');
reslut.status = 403.3;
}
else {
// 保存完成后的回调函数
console.log("保存完成");
reslut.status = 200;
//fs.mkdir('../public/json/blog-information/');
// var filename = path.join(__dirname, '/../public/json/blog-information/'+username+'.json');
// fs.mkdir(filename,function (err) {});
}
res.writeHead(200, { 'content-Type': 'text/plain' });
res.end(JSON.stringify(reslut));
});
}
});
}
function insertData1(pathname, postData, res) {
var reslut = { "status": "" };
// 查看源文件中是否有内容
fs.readFile(pathname, function (err, data) {
if (err)
send.send500(res);
else {
if (data == "") { // 如果文件为空,则写入格式如下
postData = JSON.parse(postData);
postData = {
"id":1,
"title":postData.title,
"content":postData.content,
"date":postData.date,
"username":postData.username
}
postData = '[' + JSON.stringify(postData) + ']';
}
else { // 如果格式不为空,则在将待写入的数据加在最后一个元素后面
data = JSON.parse(data);
postData = JSON.parse(postData);
postData = {
"id":data.length+1,
"title":postData.title,
"content":postData.content,
"date":postData.date,
"username":postData.username
}
data = JSON.stringify(data);
postData = '['+JSON.stringify(postData)+ ',' +data.slice(1, data.length);
}
fs.writeFile(pathname, postData, 'utf8', function (err) {
if (err) {
console.log('保存失败');
reslut.status = 403.3;
}
else {
// 保存完成后的回调函数
console.log("保存完成");
reslut.status = 200;
}
res.writeHead(200, { 'content-Type': 'text/plain' });
res.end(JSON.stringify(reslut));
});
}
});
}
exports.writeData = writeData;
exports.insertData = insertData;
exports.insertData1 = insertData1;
index.js
var app = angular.module('myApp', ['ngRoute']);
// 路由控制
app.config(['$routeProvider', function ($routeProvider) {
$routeProvider
.when('/', {
controller: 'blogListCtrl',
templateUrl: '../views/blog-list.html'
})
.when('/login', {
controller: 'loginCrtl',
templateUrl: '../views/login.html'
})
.when('/signup', {
controller: 'signupCrtl',
templateUrl: '../views/signup.html'
})
.when('/bloglist', {
controller: 'blogListCtrl',
templateUrl: '../views/blog-list.html'
})
.when('/personalhome', {
controller: 'personalCtrl',
templateUrl: '../views/personal-homepage.html'
})
.when('/getArticleDetails/:articleInfo', {
controller: 'articleDetailCtrl',
templateUrl: '../views/blog-item.html'
})
.when('/writeblog', {
controller: 'writeBlogCtrl',
templateUrl: '../views/write-blog.html'
})
.otherwise({
redirectTo: '/'
});
}]);
// 主页控制器
app.controller("homeCtrl", ['$scope', '$rootScope', '$location', function ($scope, $rootScope, $location) {
// 一些标记,记录状态
$rootScope.isLogout = true;
$rootScope.isLogin = false;
$rootScope.isMyblog = false;
// 点击登陆按钮登陆
$scope.login = function () {
$location.path('/login').replace();
}
// 注册按钮
$scope.signup = function () {
$location.path('/signup').replace();
}
// 退出按钮
$scope.logout = function () {
alert('确定退出');
$rootScope.isLogout = true;
$rootScope.isLogin = false;
$location.path('/').replace();
}
}]);
// 登录页控制器
app.controller("loginCrtl", ['$scope', '$rootScope', '$http', '$location', function ($scope, $rootScope, $http, $location) {
$scope.dataSumbit = function () {
if ($scope.myForm.$valid) { // 表单验证成功
$http({
method: 'post',
url: '/login',
data: $scope.user
}).success(function (data, status, header, cfg) {
if (data.status == 200) { // 验证成功
$rootScope.isLogout = false;
$rootScope.isLogin = true;
$rootScope.user = $scope.user;
$location.path('/').replace();
}
else if (data.status == 401.1) { // 验证失败
alert('用户名或密码错误!请重新输入');
$location.path('/login').replace();
}
}).error(function (data, status, header, cfg) {
alert(status);
});
}
}
}]);
// 注册页控制器(待实现。。。)
app.controller("signupCrtl", ['$scope', '$http', '$location', function ($scope, $http, $location) {
$scope.signUserInfo = {
"username": "",
"password": "",
"confirmPassword": ""
}
$scope.dataSumbit = function () {
if ($scope.myForm.$valid) { // 表单验证成功
$http({
method: 'post',
url: '/signup',
data: { "username": $scope.signUserInfo.username, "password": $scope.signUserInfo.password }
}).success(function (data, status, header, cfg) {
if (data.status == 200) { // 注册成功
alert('注册成功');
$location.path('/login').replace();
}
else if (data.status == 403.3) { // 注册失败
alert('注册失败');
$location.path('/signup').replace();
}
}).error(function (data, status, header, cfg) {
alert(status);
});
}
}
}]);
// 首页控制器
app.controller('blogListCtrl', ['$scope', '$rootScope', '$http', '$location', '$filter', function ($scope, $rootScope, $http, $location, $filter) {
$rootScope.isMyblog = false;
$http({// 与后端交互,获取数据
method: 'GET',
url: '/getBlogList'
}).success(function (data, status, header, cfg) {
$scope.blogItems = data;
if ($scope.blogItems.length === 0)
$scope.isNull = true;
else {
$scope.isNull = false;
for (var i = 0; i < $scope.blogItems.length; i++) {
// 简单处理:显示博客部分内容 ,日期进行格式化
$scope.blogItems[i].content = $scope.blogItems[i].content.slice(0, 10) + "...";
$scope.blogItems[i].date = $filter('date')($scope.blogItems[i].date, "yyyy-MM-dd hh:mm:ss");
}
}
}).error(function (data, status, header, cfg) {
alert(status);
});
//与个人主页页面逻辑相同
$scope.blogClickHandler = function (index) {
if ($rootScope.isLogin && !$rootScope.isLogout) {
$scope.user = $rootScope.user;
$rootScope.isMyblog = true;
var articleInfo = $scope.blogItems[index].id + '|' + $scope.blogItems[index].username;
$location.path('/getArticleDetails/' + articleInfo);
}
else {
alert('您还未登录,请先登录');
$location.path('/login').replace();
}
}
$scope.changeOver = function (index) {
$scope.over = index;
}
}]);
// 个人主页控制器
app.controller('personalCtrl', ['$scope', '$rootScope', '$http', '$location', '$filter', function ($scope, $rootScope, $http, $location, $filter) {
if ($rootScope.isLogin && !$rootScope.isLogout) {
$scope.user = $rootScope.user;
$rootScope.isMyblog = true;
$http({// 与后端交互,获取数据
method: 'post',
url: '/getPersonalhome',
data: $scope.user
}).success(function (data, status, header, cfg) {
$scope.blogItems = data;
if ($scope.blogItems.length == 0)
$scope.isNull = true;
else {
$scope.isNull = false;
for (var i = 0; i < $scope.blogItems.length; i++) {
// 简单处理:显示博客部分内容 ,日期进行格式化
$scope.blogItems[i].content = $scope.blogItems[i].content.slice(0, 10) + "...";
$scope.blogItems[i].date = $filter('date')($scope.blogItems[i].date, "yyyy-MM-dd hh:mm:ss");
}
}
}).error(function (data, status, header, cfg) {
alert(status);
});
$scope.blogClickHandler = function (index) {
var articleInfo = $scope.blogItems[index].id + '|' + $scope.blogItems[index].username;
$location.path('/getArticleDetails/' + articleInfo);
}
$scope.changeOver = function (index) {
$scope.over = index;
}
$scope.btnWtieBlogs = function () {
$location.path('/writeblog').replace();
}
} else {
alert('您还未登录,请先登录');
$location.path('/login').replace();
}
}]);
// 博客详情页
app.controller('articleDetailCtrl', ['$scope', '$routeParams', '$http', '$filter', function ($scope, $routeParams, $http, $filter) {
$scope.articleInfo = $routeParams.articleInfo;
var info = $scope.articleInfo.split('|');
$scope.postData = {
"id": info[0],
"username": info[1]
}
$http({// 与后端交互,获取数据
method: 'post',
url: '/getArticleDetails',
data: $scope.postData
}).success(function (data, status, header, cfg) {
$scope.article = data;
$scope.article.date = $filter('date')($scope.article.date, "yyyy-MM-dd hh:mm:ss");
}).error(function (data, status, header, cfg) {
alert(status);
});
}]);
// 写博客页面
app.controller('writeBlogCtrl', ['$scope', '$rootScope', '$http', '$location', '$interval', function ($scope, $rootScope, $http, $location, $interval) {
if ($rootScope.isLogin && !$rootScope.isLogout) {
$rootScope.isMyblog = true;
$scope.user = $rootScope.user;
// 需要初始化,不然在第一次提交时,若博客标题或者内容为空,会出现title未定义的错误
$scope.blog = {
"title": "",
"content": "",
"date": ""
}
$scope.blog.date = Date.parse(new Date());
$interval(function () {
$scope.blog.date = Date.parse(new Date());
}, 1000);
$scope.blogPublish = function () {
if ($scope.blog.title !== "" && $scope.blog.content !== "") {
$http({
method: 'post',
url: '/postBlogs',
data: { user: $scope.user, blog: $scope.blog }
}).success(function (data, status, header, cfg) {
if (data.status === 200) {
alert('提交成功');
$location.path('/writeblog').replace();
}
else if (data.status === 403.3) {
alert('提交失败');
$location.path('/writeblog').replace();
}
}).error(function (data, status, header, cfg) {
alert(status);
});
}
else {
alert("博客标题或者内容不能为空");
}
}
$scope.blogListReview = function () {
$location.path('/personalhome').replace();
}
}
else {
alert('您还未登录,请先登录');
$location.path('/login').replace();
}
}]);
// 自定义验证用户名的指令
app.directive("usernameValidate", function () {
return {
restrict: 'A',
require: 'ngModel',
link: function (scope, elem, attrs, ctrl) {
var uNameRegexp = /^(?![0-9]+$)[a-zA-Z_][a-zA-Z_0-9]{8,20}$/; // 不能以数字开头,可包含数字字母下划线,长度6-20位
var phoneRegexp = /^1[3|5|8][0-9]\d{4,8}$/; // 11位手机号
var emailRegexp = /^([a-z0-9]*[-_.]?[a-z0-9]+)+@([a-z0-9]*[-_]?[a-z0-9]+)+[\.][a-z]{2,3}([\.][a-z]{2})?$/;
var isPhone = /^\d{11,11}$/; // 11位的纯数字
var isEmail = /[\@]/; // 包含@
// 正则验证
ctrl.$parsers.unshift(function (viewValue) {
if (isPhone.test(viewValue)) {//纯数字
if (phoneRegexp.test(viewValue))
ctrl.$setValidity('formatValid', true);
else
ctrl.$setValidity('formatValid', false);
} else if (isEmail.test(viewValue)) {
if (emailRegexp.test(viewValue))
ctrl.$setValidity('formatValid', true);
else
ctrl.$setValidity('formatValid', false);
} else {
if (uNameRegexp.test(viewValue))
ctrl.$setValidity('formatValid', true);
else
ctrl.$setValidity('formatValid', false);
}
return viewValue;
});
ctrl.$render = function () { };
}
}
});
// 自定义用户名唯一性验证指令
app.directive("usernameUnique", function ($http) {
return {
restrict: 'A',
require: 'ngModel',
link: function (scope, elem, attrs, ctrl) {
scope.$watch(attrs.ngModel, function (viewValue) {
$http({
method: 'post',
url: '/uniqueValidate',
data: { "username": viewValue }
}).success(function (data, status, header, cfg) {
ctrl.$setValidity('unique', data.isUnique);
}).error(function (data, status, header, cfg) {
ctrl.$setValidity('unique', false);
});
});
}
}
});
// 自定义密码一致性验证指令
app.directive("passwordRepeat", function () {
return {
restrict: 'A',
require: 'ngModel',
link: function (scope, elem, attrs, ctrl) {
var otherInput = elem.inheritedData("$formController")[attrs.passwordRepeat];
ctrl.$parsers.push(function (value) {
if (value === otherInput.$viewValue) {
ctrl.$setValidity("repeat", true);
return value;
}
ctrl.$setValidity("repeat", false);
});
otherInput.$parsers.push(function (value) {
ctrl.$setValidity("repeat", value === ctrl.$viewValue);
return value;
});
}
}
});
userinfo.json
[{"username":"2910776796@qq.com","password":"12345678"},{"username":"suella123","password":"12345678"},{"username":"zhangsan123","password":"12345678"}]
blog-item.css
.article-title {
font-size: 22px;
font-weight: 700;
margin: 8px 0;
display: inline-block;
vertical-align: bottom;
width: 75%;
line-height: 130%;
}
.info{
font-size: 12px;
color: #838383;
}
.info:before {
content: "•";
}
.article-content{
margin: 0 10px;
line-height: 2em;
font-family: "Helvetica Neue","Luxi Sans","DejaVu Sans",Tahoma,"Hiragino Sans GB",STHeiti,sans-serif!important;
font-size: 14px;
word-break: break-word;
overflow: auto;
}
hr{
background: rgb(236, 235, 235);
border: none;
height: 1px;
}
index.css
html{
height:100%;
font-size: 62.5%;
-webkit-tap-highlight-color: rgba(0,0,0,0);
}
body .toolbar .container {
width: 100%;
}
body,ul,p,span,div,a,input,button{
margin:0;
padding:0;
box-sizing:border-box;
-moz-box-sizing:border-box;
-webkit-box-sizing:border-box
}
li{
padding: 18px,24px;
list-style: none;
}
body{
min-width:1116px;
line-height:24px;
font-size:16px;
background:white;
}
.toolbar {
font-family: 'PingFang SC','Microsoft YaHei',SimHei,Arial,SimSun;
font-size: 1em;
color: #333;
line-height: 1.5em;
background: #fff;
box-shadow: 0 2px 4px 0 rgba(0,0,0,.05);
min-width: 1200px;
width: 100%;
height: 50px;
box-sizing: border-box;
-moz-box-sizing: border-box;
-webkit-box-sizing: border-box;
}
.nav-pagebar{
float:left;
width: 40%;
position: relative;
margin-top: 13px;
}
.pagebar{
margin-left: 10px;
margin-right: 10px;
list-style:none;
display:inline;
}
.user-infomation{
position: relative;
float: left;
margin-left: 10px;
margin-top: 10px;
width: 40%;
}
.infomation{
margin:-1px;
}
.logarea{
float: right;
width: 10%;
}
.logbutton,.loginfo{
position: relative;
float: right;
text-align: right;
font-size: 0.75em;
}
.btnlogin,.btnsignup,.btnlogout{
border:0;
background-color:#fff;
}
.container{
background-color: #f5f6f7;
width: 100%;
height: 1000px;
}
.nav-menu {
float: left;
width: 10%;
position: relative;
padding: 10px;
margin: 10px;
/* background-color: #fff; */
text-align: center;
line-height: 32px;
/* -webkit-box-shadow: 0 1px 2px 0 rgba(0,0,0,0.04);
box-shadow: 0 1px 2px 0 rgba(0,0,0,0.04); */
}
.main-content{
float: left;
width: 69%;
position: relative;
margin:10px;
background: #fff;
-webkit-box-shadow: 0 1px 2px 0 rgba(0,0,0,0.04);
box-shadow: 0 1px 2px 0 rgba(0,0,0,0.04);
height: 100%;
}
.right-box {
position: relative;
float: right;
width: 16%;
padding: 10px;
margin-top: 10px;
margin-right: 10px;
margin-bottom: 10px;
/* background: #fff; */
/* -webkit-box-shadow: 0 1px 2px 0 rgba(0,0,0,0.04);
box-shadow: 0 1px 2px 0 rgba(0,0,0,0.04); */
}
.container::after{
content: '.';
height: 0;
visibility: hidden;
display: block;
clear: both;
}
a{
outline:none;
text-decoration:none;
}
a:link,a:visited{
color:#0f0f0f;
}
a:hover,a:active,a:focus{
color:red;
}
.wrapper1,.wrapper11,.form-wrapper,.parent,.child{
margin: 0px;
padding: 0px;
}
.wrapper1{
float: left;
position: relative;
left: 30%;
top: 20%;
width:40%;
height:400px;
}
.wrapper11{
position: relative;
width: 100%;
height: 100%;
background-color:rgba(238, 236, 222, 0.897);
border: 1px solid rgb(239, 241, 241);
}
.form-wrapper{
position: relative;
width:80%;
height: 80%;
top:50%;
left:50%;
}
.parent{
position: relative;
width:100%;
height: 100%;
top:-50%;
left:-50%;
background-color:white;
}
.child{
position: relative;
width:90%;
height: 55px;
margin-bottom: 5px;
}
.login-header{
text-align: center;
}
.p-login-header{
position: relative;
top:25%;
font-size: 1.4em;
}
.showError{
margin-top: 0;
margin-bottom: 0;
padding: 0;
font-size: 11px;
color: red;
line-height: 0;
}
.chbx{
height: 13px;
font-size: 8px;
color: darkgray;
}
.child,.btn-login,.showError {
margin-left:15px;
margin-right:15px;
}
.chk{
margin-left:15px;
}
.btn-login {
box-sizing: content-box;
border: 2px solid #3762bc;
padding: 0;
background-color: #4a77d4;
color: white;
}
person
.p-list-item{
margin-bottom:-1px;
/* height: 120px; */
border-bottom: 1px dotted lightgrey;
border-top: 1px dotted lightgrey;
}
.list-head,.list-body,.list-foot{
margin-top:8px;
margin-bottom:8px;
margin-left: 10px;
margin-right:0px;
}
.name{
position: relative;
float: left;
}
.p-list-username,.p-list-publish-time{
font-size: 0.7em;
}
.p-list-publish-time{
text-align: end;
}
.null-show{
position: relative;
width: 100%;
height: 1000px;
text-align: center;
}
.null-show-info{
margin:0;
margin-top:50px;
margin-bottom:50px;
position: relative;
width:100%;
text-align: center;
font-size: 16px;
}
.btn-write-blogs{
position: relative;
width: 40%;
height: 5%;
display: inline-block;
font-weight: 400;
text-align: center;
white-space: nowrap;
vertical-align: middle;
-webkit-user-select: none;
-moz-user-select: none;
-ms-user-select: none;
user-select: none;
border: 1px solid transparent;
padding: .375rem .75rem;
font-size: 1em;
line-height: 1.5;
border-radius: .25rem;
transition: color .15s ease-in-out,background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out;
color: #6c757d;
background-color: transparent;
background-image: none;
border-color: #6c757d;
margin-left: 0;
}
signup
.wrapper2,.wrapper22,.form-wrapper2,.parent2,.child2{
margin: 0px;
padding: 0px;
}
.wrapper2{
float: left;
position: relative;
left: 20%;
top: 15%;
width:60%;
height:500px;
}
.wrapper22{
position: relative;
width: 100%;
height: 100%;
background-color:rgba(238, 236, 222, 0.897);
border: 1px solid rgb(239, 241, 241);
}
.form-wrapper2{
position: relative;
width:80%;
height: 80%;
top:50%;
left:50%;
}
.parent2{
position: relative;
width:100%;
height: 100%;
top:-50%;
left:-50%;
background-color:white;
}
.child2{
position: relative;
width:90%;
height: 55px;
margin-bottom: 5px;
}
.login-header2{
text-align: center;
}
.p-login-header2{
position: relative;
top:25%;
font-size: 1.4em;
}
.showError2{
margin-top: 0;
margin-bottom: 0;
padding: 0;
font-size: 11px;
color: red;
line-height: 0;
}
.child2,.btn-login2,.showError2 {
margin-left:15px;
margin-right:15px;
}
.btn-signup2{
margin-top: 5px;
box-sizing: content-box;
border: 2px solid #3762bc;
padding: 0;
background-color: #4a77d4;
color: white;
}
writeblog
.write-blog-wrapper{
position: relative;
width: 100%;
}
.blog-box-title,.blog-box-content,.blog-box-button{
position: relative;
width: 95%;
margin-left:20px;
margin-right:20px;
margin-bottom:20px;
}
.blog-box-title{
margin-top: 20px;
height: 40px;
border: 1px solid transparent;
}
.blog-box-content{
height: 400px;
border: 1px solid transparent;
}
.txt-blog-title,.txt-blog-content{
position: relative;
width: 100%;
height: 100%;
}
.blog-box-button{
text-align: center;
height: 50px;
}
.btnblog{
position: relative;
width: 15%;
height: 100%;
display: inline-block;
font-weight: 400;
text-align: center;
white-space: nowrap;
vertical-align: middle;
-webkit-user-select: none;
-moz-user-select: none;
-ms-user-select: none;
user-select: none;
border: 1px solid transparent;
padding: .375rem .75rem;
font-size: 1em;
line-height: 1.5;
border-radius: .25rem;
transition: color .15s ease-in-out,background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out;
color: #6c757d;
background-color: transparent;
background-image: none;
border-color: #6c757d;
margin-left: 10px;
margin-right: 10px;
}
blog-item
<div class="header">
<div class="title">
<span class ="article-title">{{article.title}}</h3>
</div>
<div class = "auth-info">
<span class="info">发布时间:{{article.date}}</span>
<span class="info">作者:{{article.username}}</spanclass>
</div>
</div>
<hr>
<div class="content">
<div>
<p class = "article-content">{{article.content}}</p>
</div>
</div>
blog-list
<div class="wrapper3">
<ul class="p-blog-list">
<li class="p-list-item" ng-repeat="content in blogItems" ng-click="blogClickHandler($index)" ng-mousemove="changeOver($index)"
ng-class="{over:$index==over}">
<div class = "list-head">
<h3 class="p-list-title">{{content.title}}</h3>
</div>
<div class="list-body">
<p class="p-list-body">{{content.content}}</p>
</div>
<div class = "list-foot">
<div class="name"><p class="p-list-username">用户:{{content.username}}</p></div>
<div class="date"><p class="p-list-publish-time">{{content.date}}</p></div>
</div>
</li>
</ul>
<div class="null-show" ng-show="isNull">
<p class="null-show-info">暂无内容</p>
</div>
</div>
index
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title>BlogSystem</title>
<link rel="stylesheet" href="../stylesheets/index.css " type="text/css" />
<link rel="stylesheet" href="../stylesheets/login.css " type="text/css" />
<link rel="stylesheet" href="../stylesheets/sigup.css " type="text/css" />
<link rel="stylesheet" href="../stylesheets/write-blog.css " type="text/css" />
<link rel="stylesheet" href="../stylesheets/personal-homepage.css " type="text/css" />
<link rel="stylesheet" href="../stylesheets/blog-item.css " type="text/css" />
<script src="../javascripts/angular.js"></script>
<script src="../javascripts/angular-route.js"></script>
</head>
<body ng-app="myApp">
<div class="toolbar" ng-controller="homeCtrl">
<div class="nav-pagebar">
<ul>
<li class="active pagebar">
<a href="#/bloglist">首页</a>
</li>
<li class="pagebar">
<a href="#/personalhome">个人主页</a>
</li>
<li class="pagebar">
<a href="#/writeblog">写博客</a>
</li>
</ul>
</div>
<div class="user-infomation" ng-show="isMyblog && isLogin">
<h3 class="infomation">{{user.username}}的个人主页</h3>
</div>
<div class="user-infomation" ng-show="!isMyblog || !isLogin">
<h3 class="infomation">简易博客系统</h3>
</div>
<div class="logarea">
<div class="logbutton">
[
<!-- <a href="#/login">登陆</a> -->
<button class="btnlogin" type="button" ng-click="login()" ng-disabled="isLogin">登陆</button>
.
<!-- <a href="#/signup">注册</a> -->
<button class="btnsignup" type="button" ng-click="signup()" ng-disabled="isLogin">注册</button>
.
<button class="btnlogout" type="button" ng-click="logout()" ng-disabled="isLogout">退出</button>
]
</div>
<div class="loginfo">
[
<label class="" ng-show="isLogout" style="color: red">抱歉,您未登录</label>
<label class="" ng-show="isLogin">欢迎,您已登录</label>
]
</div>
</div>
</div>
<div class="container clearfix">
<nav class="nav-menu">
</nav>
<div ng-view class="main-content"></div>
<div class="right-box">
</div>
<script src="../javascripts/index.js"></script>
</body>
</html>
login
<div class="wrapper1">
<div class="wrapper11">
<div class="form-wrapper">
<form name="myForm" class="parent" ng-submit="dataSumbit()" novalidate>
<div class="child login-header">
<p class="p-login-header">用户登录</p>
</div>
<input class="child userinfo" type="text" name="userName" placeholder="用户名/手机号/邮箱" ng-model="user.username" username-Validate required>
</input>
<span class="showError" ng-show="myForm.userName.$dirty && myForm.userName.$touched">
<span ng-show="myForm.userName.$error.required">用户名不能为空</span>
<span ng-show="myForm.userName.$error.formatValid">用户名格式错误。</span>
</span>
<br>
<input class="child userinfo" type="password" name="password" placeholder="密码" ng-model="user.password" required>
</input>
<span class="showError" ng-show="myForm.password.$dirty && myForm.password.$touched">
<span ng-show="myForm.password.$error.required">密码不能为空</span>
</span>
<br>
<input class="chk" type="checkbox" name="autologin" value="no">
<span class="chbx">下一次自动登录</span>
<br>
<button type="submit" class="child btn-login" name="login">登录</button>
<br>
</form>
</div>
</div>
</div>
person
<div class="wrapper3">
<ul class="p-blog-list">
<li class="p-list-item" ng-repeat="content in blogItems" ng-click="blogClickHandler($index)" ng-mousemove="changeOver($index)"
ng-class="{over:$index==over}">
<div class="list-head">
<h3 class="p-list-title">{{content.title}}</h3>
</div>
<div class="list-body">
<p class="p-list-body">{{content.content}}</p>
</div>
<div class="list-foot">
<div class="name">
<p class="p-list-username">用户:{{content.username}}</p>
</div>
<div class="date">
<p class="p-list-publish-time">{{content.date}}</p>
</div>
</div>
</li>
</ul>
<div class="null-show" ng-show="isNull">
<p class="null-show-info">空空如也~ 快写你的第一个博客吧</p>
<input class="btn-write-blogs" type="button" ng-click="btnWtieBlogs()" value="写博客" />
</div>
</div>
sigup
<div class="wrapper2">
<div class="wrapper22">
<div class="form-wrapper2">
<form name="myForm" class="parent2" ng-submit="dataSumbit()" novalidate>
<div class="child2 login-header2">
<p class="p-login-header2">用户注册</p>
</div>
<input class="child2 userinfo2" type="text" name="userName" placeholder="用户名/手机号/邮箱" ng-model="signUserInfo.username" username-Validate
username-Unique required />
<span class="showError2" ng-show="myForm.userName.$dirty && myForm.userName.$touched">
<span ng-show="myForm.userName.$error.required">用户名不能为空</span>
<span ng-show="myForm.userName.$error.formatValid">用户名格式错误。</span>
<span ng-show="myForm.userName.$error.unique">用户名已存在</span>
</span>
<br>
<input id="password" class="child2 userinfo2" type="password" name="password" placeholder="请输入密码" ng-model="signUserInfo.password"
required />
<span class="showError2" ng-show="myForm.password.$dirty && myForm.password.$touched">
<span ng-show="myForm.password.$error.required">密码不能为空</span>
</span>
<br>
<input id="confirmPassword" class="child2 userinfo2" type="password" name="confirmPassword" placeholder="再次输入密码" ng-model="signUserInfo.confirmPassword"
password-Repeat="password" required>
</input>
<span class="showError" ng-show="myForm.confirmPassword.$dirty && myForm.confirmPassword.$touched">
<span ng-show="myForm.confirmPassword.$error.required">密码不能为空</span>
<span ng-show="myForm.confirmPassword.$error.repeat">两次密码不一致</span>
</span>
<br>
<button type=" submit " class="child2 btn-signup2" name="sigunup ">注册</button>
<br>
</form>
</div>
</div>
</div>
write
<div class="write-blog-wrapper">
<div class="blog-box-title">
<input class="txt-blog-title" type="text" placeholder="请输入文章标题" ng-model="blog.title" />
</div>
<div class="blog-box-content">
<textarea class="txt-blog-content" clos=",50" rows="5" warp="virtual" placeholder="在此编辑博客内容" ng-model="blog.content"></textarea>
</div>
<div class="blog-box-button">
<input class="btnblog" type="button" ng-click = "blogPublish()" value="发布博客">
<input class="btnblog" type="button" ng-click = "blogListReview()" value="查看列表">
</div>
</div>
server
var http = require('http');
var url = require('url');
var path = require('path');
var static = require('./lib/static-request-handler');
var dynamic = require('./lib/dynamic-request-handler');
var mime =require('./lib/mime-type');
var PORT = 3000;
var server = http.createServer(onRequest);
//服务器监听程序
function onRequest(req, res) {
var pathname = url.parse(req.url).pathname;
// 获取后缀名并解析为相应的文件类型
var suffix = path.extname(pathname);
// 根据后缀名区分静态资源和动态资源请求
if (suffix != null && suffix.trim() != "") {
var contentType = mime.getContentType(suffix);
static.staticReqHandler(pathname, contentType, res);
} else {
dynamic.dynamicReqHandler(pathname, req, res);
}
}//onRequest
server.listen(PORT);
console.log("Server runing at port: " + PORT + ".");
改进: var fs = require('fs'); var send = require('./send-page'); var path = require('path')
var createFolder = function (filename) { var sep = path.sep var folders = path.dirname(filename).split(sep); var p = ''; while (folders.length) { p += folders.shift() + sep; if (!fs.existsSync(p)) { fs.mkdirSync(p); } } };
function writeData(pathname, req, res) { var postData = '';
req.on('data', function (chunk) {
postData += chunk;
});
req.on('end', function () {
insertData(pathname, postData, res)
});
}
function insertData(pathname, postData, res) {
var reslut = { "status": "" };
var username = JSON.parse(postData).username;
// 查看源文件中是否有内容
fs.readFile(pathname, function (err, data) {
if (err)
send.send500(res);
else {
if (data == "") { // 如果文件为空,则写入格式如下
postData = '[' + postData + ']';
}
else { // 如果格式不为空,则在将待写入的数据加在最后一个元素后面
var buf = new Buffer(data);
postData = buf.slice(0, buf.length - 1) + ',' + postData + ']';
}
fs.writeFile(pathname, postData, 'utf8', function (err) {
if (err) {
console.log('保存失败');
reslut.status = 403.3;
isSuccess = false;
}
else {
// 保存完成后的回调函数
console.log("保存完成");
reslut.status = 200;
var filename = path.join(__dirname, '/../public/json/blog-information/'+username + '.json');
createFolder(filename);
fs.createWriteStream(filename);
}
res.writeHead(200, { 'content-Type': 'text/plain' });
res.end(JSON.stringify(reslut));
});
}
});
}
function insertData1(pathname, postData, res) {
var reslut = { "status": "" };
// 查看源文件中是否有内容
fs.readFile(pathname, function (err, data) {
if (err)
send.send500(res);
else {
if (data == "") { // 如果文件为空,则写入格式如下
postData = JSON.parse(postData);
postData = {
"id": 1,
"title": postData.title,
"content": postData.content,
"date": postData.date,
"username": postData.username
}
postData = '[' + JSON.stringify(postData) + ']';
}
else { // 如果格式不为空,则在将待写入的数据加在最后一个元素后面
data = JSON.parse(data);
postData = JSON.parse(postData);
postData = {
"id": data.length + 1,
"title": postData.title,
"content": postData.content,
"date": postData.date,
"username": postData.username
}
data = JSON.stringify(data);
postData = '[' + JSON.stringify(postData) + ',' + data.slice(1, data.length);
}
fs.writeFile(pathname, postData, 'utf8', function (err) {
if (err) {
console.log('保存失败');
reslut.status = 403.3;
}
else {
// 保存完成后的回调函数
console.log("保存完成");
reslut.status = 200;
}
res.writeHead(200, { 'content-Type': 'text/plain' });
res.end(JSON.stringify(reslut));
});
}
});
}
exports.writeData = writeData; exports.insertData = insertData; exports.insertData1 = insertData1;
index.js
var fs = require('fs');
var path = require('path');
var write = require('./write-data');
var send = require('./send-page');
const util = require('./util');
function dynamicReqHandler(pathname, req, res) {
switch (pathname) {
case "/":
pathname = "./public/views/index.html";
send.sendPage(pathname, 'text/html', res);
break;
case "/login":
login(req, res);
break;
case "/signup":
signup(req, res);
break;
case "/uniqueValidate":
uniqueValidate(req, res);
break;
case "/postBlogs":
postBlog(req, res);
break;
case "/getBlogList":
getList(res);
break;
case "/getPersonalhome":
getPersonalHomePage(req, res);
break;
case "/getArticleDetails":
getArticleDetails(req, res);
break;
default:
send.send500(res);
}
}
// 用户登录验证
function login(req, res) {
console.log("Request handler 'login' was called.");
var postData = '';
// 通过req的data事件监听函数,每当接受到请求体的数据,就累加到postData变量中
req.on('data', function (chunk) {
postData += chunk;
});
// 在end事件触发后然后向客户端返回。
req.on('end', function () {
var reslut = { 'status': '' };
postData = JSON.parse(postData);
fs.readFile('./public/json/user-infomation.json', function (err, userInfo) {
if (err)
send.send500(res);
else {
if(userInfo !=""){
userInfo = JSON.parse(userInfo);
let flag = util.userExists(userInfo, postData);
if (flag >= 0)
reslut.status = 200;
else
reslut.status = 401.1;
res.writeHead(200, { 'content-Type': 'text/plain' });
res.end(JSON.stringify(reslut));
}else{
reslut.status = 401.1;
res.writeHead(200, { 'content-Type': 'text/plain' });
res.end(JSON.stringify(reslut));
}
}
});
});
}
// 注册
function signup(req, res) {
console.log("Request handler 'signup' was called.");
pathname = './public/json/user-infomation.json';
write.writeData(pathname, req, res);
}
// 验证用户名是否唯一
function uniqueValidate(req, res) {
console.log("Request handler 'uniqueValidate' was called.");
var postData = '';
// 通过req的data事件监听函数,每当接受到请求体的数据,就累加到postData变量中
req.on('data', function (chunk) {
postData += chunk;
});
// 在end事件触发后然后向客户端返回。
req.on('end', function () {
postData = JSON.parse(postData);// 解析接收的浏览器请求
console.log(postData);
// 返回给客户端的状态数据
var reslut = { 'isUnique': '' };
fs.readFile('./public/json/user-infomation.json', function (err, userInfo) {
if (err)
send.send500(res);
else {
if (userInfo != "") {
userInfo = JSON.parse(userInfo);
var flag = false;
for (var i = 0; i < userInfo.length; i++) {
if (userInfo[i].username === postData.username)
flag = true;
}
if (flag) // 存在,不唯一
reslut.isUnique = false;
else // 不存在,唯一
reslut.isUnique = true;
res.writeHead(200, { 'content-Type': 'text/plain' });
res.end(JSON.stringify(reslut));
}
}
});
});
}
// 提交博客数据
function postBlog(req, res) {
console.log("Request handler 'postBlogs' was called.");
var postData = '';
req.on('data', function (chunk) {
postData += chunk;
});
req.on('end', function () {
postData = JSON.parse(postData);
fs.readFile('./public/json/user-infomation.json', function (err, userInfo) {
if (err)
send.send500(res);
else {
let pathname = util.getPath(userInfo, postData.user);
postData.blog.username = postData.user.username;
write.insertBlogInfo(pathname, postData.blog, res);
}
});
});
}
// 首页读取JSON数据,显示博客列表(修改,显示所有用户的最新10条)
function getList(res) {
console.log("Request handler 'getList' was called.");
dirname = path.join(__dirname, '/../public/json/blog-information/');
let fileNames = util.findSync(dirname);
var datas = [];
for (let i = 0; i < fileNames.length; i++) {
datas[i] = fs.readFileSync(fileNames[i], 'utf-8');
}
var copyDatas = util.getNotNullFile(datas);
var blogData = "";
if (copyDatas.length != 0) {
var blogData = copyDatas[0].slice(0, copyDatas[0].length - 1);
for (let i = 1; i < copyDatas.length; i++) {
blogData += ',' + copyDatas[i].slice(1, copyDatas[i].length - 1);
}
blogData += ']';
// 以date降序重新排列blogData
blogData = JSON.parse(blogData).sort(util.keySort("date"));
res.writeHead(200, { 'content-Type': 'application/json' });
res.end(JSON.stringify(blogData));
}
else {
blogData = "[]";
res.writeHead(200, { 'content-Type': 'application/json' });
res.end(blogData);
}
}
// 读取个人主页数据
function getPersonalHomePage(req, res) {
console.log("Request handler 'getPersonalHomePage' was called.");
var postData = '';
req.on('data', function (chunk) {
postData += chunk;
});
// 在end事件触发后然后向客户端返回。
req.on('end', function () {
postData = JSON.parse(postData);// 解析接收的浏览器请求
// 根据用户名区分是哪个用户的主页数据,读取相应文件
fs.readFile('./public/json/user-infomation.json', function (err, userInfo) {
if (err)
send.send500(res);
else {
var pathname = util.getPath(userInfo, postData);
send.sendPage(pathname, "application/json", res);
}
});
});
}
// 获取博客详情
function getArticleDetails(req, res) {
console.log("Request handler 'getArticleDetails' was called.");
var postData = '';
req.on('data', function (chunk) {
postData += chunk;
});
req.on('end', function () {
postData = JSON.parse(postData);
let pathname = './public/json/blog-information/' + postData.username + '.json';
blogData = JSON.parse(fs.readFileSync(pathname, 'utf-8'));
res.writeHead(200, { 'content-type': "application/json" });
res.end(JSON.stringify(blogData[blogData.length - postData.id]));
});
}
exports.dynamicReqHandler = dynamicReqHandler;
util.js
const fs = require('fs');
const path = require('path');
// 获取非空文件列表
function getNotNullFile(arr) {
var copyArr = [];
for (let i = 0, j = 0; i < arr.length; i++) {
if (arr[i] !== "") {
copyArr[j++] = arr[i];
}
}
return copyArr;
}
// 查看用户是否存在
function userExists(userInfo, postData) {
let flag = -1;
for (let i = 0; i < userInfo.length; i++) {
if (userInfo[i].username === postData.username && userInfo[i].password === postData.password) {
flag = i;
}
}
return flag;
}
// 获取对应用户的存储路径
function getPath(userInfo, postData) {
userInfo = JSON.parse(userInfo);
let index = userExists(userInfo, postData);
if (index >= 0) {
pathname = './public/json/blog-information/' + userInfo[index].username + '.json';
}
return pathname;
}
// 获取指定目录下的文件
function findSync(startPath) {
let fileNames = [];
function finder(path) {
let files = fs.readdirSync(path);
files.forEach((val) => {
let fPath = path + val;
// 获取文件信息(对象)
let stats = fs.statSync(fPath);
if (stats.isDirectory())
finder(fPath);
if (stats.isFile())
fileNames.push(fPath);
});
}
finder(startPath);
return fileNames;
}
// 新建文件
function createFolder(filename) {
let sep = path.sep
let folders = path.dirname(filename).split(sep);
let p = '';
while (folders.length) {
p += folders.shift() + sep;
if (!fs.existsSync(p)) {
fs.mkdirSync(p);
}
}
};
// 将JSON数组按照某个属性排序
function keySort(property) {
return function (obj1, obj2) {
// 降序 return 1,降序 return-1
return obj2[property] - obj1[property];
}
}
exports.getNotNullFile = getNotNullFile;
exports.userExists = userExists;
exports.getPath = getPath;
exports.findSync = findSync;
exports.createFolder = createFolder;
exports.keySort = keySort;
write.js
var fs = require('fs');
var send = require('./send-page');
var path = require('path');
const util = require('./util');
function writeData(pathname, req, res,handler) {
var postData = '';
req.on('data', function (chunk) {
postData += chunk;
});
req.on('end', function () {
insertUserInfo(pathname, postData, res);
});
}
function insertUserInfo(pathname, postData, res) {
var reslut = { "status": "" };
var username = JSON.parse(postData).username;
// 查看源文件中是否有内容
fs.readFile(pathname, function (err, fileData) {
if (err)
send.send500(res);
else {
if (fileData == "") { // 如果文件为空,则写入格式如下
postData = '[' + postData + ']';
}
else { // 如果格式不为空,则将待写入的数据加在最后一个元素后面
postData = fileData.slice(0, fileData.length - 1) + ',' + postData + ']';
}
fs.writeFile(pathname, postData, 'utf8', function (err) {
if (err) {
console.log('保存失败');
reslut.status = 403.3;
}
else {
// 保存完成后的回调函数
console.log("保存完成");
reslut.status = 200;
var filename = path.join(__dirname, '/../public/json/blog-information/'+username + '.json');
util.createFolder(filename);
fs.createWriteStream(filename);
}
res.writeHead(200, { 'content-Type': 'text/plain' });
res.end(JSON.stringify(reslut));
});
}
});
}
function insertBlogInfo(pathname, postData, res) {
var reslut = { "status": "" };
// 查看源文件中是否有内容
fs.readFile(pathname, function (err, fileData) {
if (err)
send.send500(res);
else {
if (fileData == "") { // 如果文件为空,则写入格式如下
postData.id = 1;
postData = '[' + JSON.stringify(postData) + ']';
}
else { // 如果格式不为空,则在将待写入的数据加在最后一个元素后面
postData.id = JSON.parse(fileData).length + 1;
postData = '[' + JSON.stringify(postData) + ',' + fileData .slice(1, fileData.length);
}
fs.writeFile(pathname, postData, 'utf8', function (err) {
if (err) {
console.log('保存失败');
reslut.status = 403.3;
}
else {
// 保存完成后的回调函数
console.log("保存完成");
reslut.status = 200;
}
res.writeHead(200, { 'content-Type': 'text/plain' });
res.end(JSON.stringify(reslut));
});
}
});
}
exports.writeData = writeData;
exports.insertUserInfo = insertUserInfo;
exports.insertBlogInfo = insertBlogInfo;
MVC(Model-View-Control)的概念以及所解决的问题相信大家都极其熟悉了,而在HTML/CSS/JS开发的页面中,一直存在的痛点在于,那些用JavaScript语言写的一陀一陀的操作DOM(即View)的代码如何去维护。 我们一般会使用jQuery库操作DOM来解决浏览器兼容性的问题,一定程度上也会简少代码量,但数据(即model)与DOM(view)的同步仍然是一个棘手的问题。
有两种将数据模型与视图绑定的方式: 一种是后端模型,即使用JSP、FTL这类的Java端模板语言,将Java模型与HTML视图绑定,在服务器端渲染成HTML推送至浏览器;还一种是前端模型,即在后端将Java模型(或其他语言开发的模型)序列化成JSON送到浏览器,浏览器端通过“某种”方式,将JSON模型更新到HTML视图(DOM)中形成最终的页面。
而在浏览器端把JSON更新到DOM中的方式,最原始的方式就是直接写JS操作DOM,包括使用jQuery这样的类库。缺点就是,这样的Web页面中,会被开发出大量的操作DOM的JavaScript代码, 而这样的代码对于后续维护和测试来说都可以说噩梦。于是出现了各种各样的在浏览器端进行数据绑定的模板语言(称为前端模板),比如ArcTemplate、EasyTemplate。忽略掉语法上细节差异, 这些模板语言基本上就是平移后端模板语言如JSP、FreeMarker的语法,在前端HTML中预留一些占位符(俗称挖坑)进行JSON数据的绑定,例如:Arctemplater的原生语法“ <div><%=i%>. <%=list[i].user%></div>”。 当然,这里JSON向DOM同步的过程需要手工执行JS调用,即这是一种非自动的单向的前端数据绑定。这就是第一代前端模板语言。
接下来,各种JS MVC框架陆续登场。以AngularJS为例:
首先,它们均使用前端模板(HTML/Template),比如“<div>Hello </div>”;
其次,提供自动的由JSON模型到DOM视图间的双向数据绑定能力,这样对于页面开发来说,就省去了那个最大的痛点,用JavaScript开发的一陀一陀操作DOM的难以维护的JS代码;
再次,AngularJS提供了指令(Directive)能力,可以扩展标准HTML属性、扩展HTML Tag标签,例如:“<input type="text" u-validator="maxLength(10)"></input>”这个例子中我们为标准的<input>标签增加了一个前端校验器的属性。
总结,JS MVC就是在一种在浏览器端使用前端模板(Html/Template)、能自动进行双向数据绑定(Json/DOM)、甚至能够扩展HTML标准属性、Tag标签的一套前端页面开发框架与技术。
Loading加载 当我们在浏览器地址栏中输入一个网址URL,或是点击页面中的一个链接新打开另一个页面,这个时候浏览器就要开始向服务器端发出HTTP请求,加载页面的html内容。 加载是一个持续的过程,现代浏览器并不会等到加载完成再去再后续解析等动作,而是加载一部分解析一部分。如果在解析的过程中,发现了其他的资源,例如:JS脚本文件、CSS文件、图片等其他资源时,会使用数量有限的独立线程去加载对应的资源。 Parse解析 当HTML文档部分或全部下载到浏览器端后,浏览器就要解析文档中的HTML标签,输出一棵与HTML标签对应的DOM对象树。这是一个复杂、反复的过程,详见《How browsers work \ HTML Parser》。 当解析到 Githubissues.
2018/7/10
AngularJS之事件
KeyDown -> KeyPress -> KeyDown -> KeyPress -> ... -> KeyUp。 KeyDown触发后,不一定触发KeyUp,当KeyDown 按下后,拖动鼠标,那么将不会触发KeyUp事件。