node Web来了
先上code
//show.js
var http =require('http');
var querystring = require('querystring');
var server = http.createServer(function(req,res){
var post='';
req.on('data',function(chunk){
post+=chunk;
});
req.on('end',function(){
post=querystring.parse(post);
res.write(post.title);
res.write(post.text);
res.end();
});
}).listen(3000);
//html
<html>
<body>
<form method="post" action="http://localhost:3000/">
<input type="text" name="title"/>
<textarea name="text"></textarea>
<input type="submit"/>
</form>
</body>
</html>
一个简单的表单提交功能实现了。
在node中可以看出,闲通过data事件接受参数缓存到post中,在end中取出post用parse分割,写回浏览器。
这用我们的想法不一致,感觉复杂得多了。相比其他的实现而言(php)。这是因为node提供的是更底层的实现。也就麻烦一点,这样我们就可以接触到更多的内容
运用Express开发
安装:
npm install -g express
安装全局的Express
建立工程:
在终端输入:
express -t ejs microblog
安装完有提示需要输入
cd microblog && npm install
自动安装ejs和express,安装指定依赖是因为package.json中内容为:
{
"name": "application-name",
"version": "0.0.1",
"private": true,
"scripts": {
"start": "node app.js"
},
"dependencies": {
"express": "3.3.3",
"jade": "*"
}
}
现在运行里面的app.js
在浏览器中输入localhost:3000就可以得到一个简单的Web to Express
看看工程的结构
主文件
app.js
/**
* Module dependencies.
*/
var express = require('express')
, routes = require('./routes')
, user = require('./routes/user')
, http = require('http')
, path = require('path');
var app = express();
//设置参数、键-值对
app.set('port', process.env.PORT || 3000);
app.set('views', __dirname + '/views');//定义模板引擎位置、__dirname的值为项目的绝对路径如(/home/zhoujixiang/node_code/microblog)
app.set('view engine', 'jade');//使用jade模板引擎
app.use(express.favicon());
app.use(express.logger('dev'));
app.use(express.bodyParser());//解析客户端请求
app.use(express.methodOverride());//用于支持定制的HTTP方法
app.use(app.router);//项目的路由支持
app.use(express.static(path.join(__dirname, 'public')));//静态文件支持
// development only
if ('development' == app.get('env')) {
app.use(express.errorHandler());//错误控制器
}
app.get('/', routes.index);//将‘/’路径映射到exports.index函数下
//index.js的主要语句是res.render('index',{title:'Express'}),功能是调用模块解析引擎
app.get('/users', user.list);
//创建一个实例
http.createServer(app).listen(app.get('port'), function(){
console.log('Express server listening on port ' + app.get('port'));
});
routes/index.js是路由文件,相当于控制器,用于组织展示的内容
index.jade、layout.jade是模板文件,即routes/index.js中调用的模板
chrome请求localhost:3000返回的Network内容
由这可以看出是发送的get请求,后面若干请求头,app会解析请求的路径,调用相应的逻辑。app.js是请求routes.index函数处理。routes.index通过res.render('index',{title:'Express'})调用视图模板index传递title变量生成视图模板HTML页面,返回给浏览器。
chrome请求localhost:3000返回的Network内容
-
Request URL:http://localhost:3000/
-
Request Method:GET
-
Status Code:200 OK
浏览器接收后发现要获取/stylesheets/style/css。再次发出请求。
app.js中并没用一个路由规则指派到/stylesheets/style/css,但app通过app.use(express.static(__dirname+'/public'))配置了静态文件服务器,会定位到/public/stylesheets/style.css
并想客户端返回
-
Request URL:http://localhost:3000/stylesheets/style.css
-
Request Method:GET
-
Status Code:304 Not Modified
创建路由规则:
当我们在地址栏输入一个http://localhost:3000/zjx之类的网址时,肯定会返回404not find的结果,因为我们没有对应的路由规则
从app.js中我们可以发现
app.get定义中定义了两个路由规则'/' 以及‘users’。所以这两个是可以访问的。
现在可以手动加入一条路由规则如:
app.get('/time',routes.time)
然后在routes/index.js,增加time函数
exports.hello=function(req,res){
res.send(‘Now Time’+ new Date().toString());
};
重启服务器,就可以访问http;//localhost:3000/time就会得到一个时间结果。
如果不想一直重启,不方便调试的话,可以用之前说的supervisor
路径匹配:
Express还支持更高级的路径匹配模式。如当定义一下路由规则时:
app.get('/user/:username',function(req,res){
res.send('user:',req.params.username);
});
访问http://localhost:3000/user/zjx
会得到
zjx的页面。
路径规则/user/:username会被自动编译为正则表达式,类似于\/user\/([^\/]+)\/?这样的实现。参数在响应函数中通过req.params的属性访问
路径规则支持js正则表达式。以便于定义更加复杂的路径规则,而不同指出是匹配的参数匿名。需要req.params[0]的形式访问。
REST风格的路由规则
REST表征状态转移,基于HTTP协议的网络应用的接口风格,充分利用HTTP的方法实现同一风格接口的服务。HTTP协议定义了一下8种标准的方法:
GET:请求获取指定资源
HEAD:请求指定资源的响应头
POST:向指定资源提交数据
PUT:请求服务器存储一个资源
DELETE:请求服务器删除指定资源
TRACE:回显服务器收到的请求,主要用于测试或诊断
CONNECT:HTTP/1.1协议中预留给能够将连接改为管道方式的代理服务器
OPTIONS:返回服务器支持的HTTP请求方法
REST设计模式中:
GET:获取 安全 幂等
POST:新增
PUT:更新 幂等
DELETE:删除 幂等
安全:指没有副作用,请求不会对资源产生变动,连续访问多次所获得的结果不受访问者的影响
幂等:重复请求多次与一次请求的效果是一样的
Express支持的HTTP请求的绑定函数
-------------------------------------------------------------------------------------------------------------------------------
请求方式 绑定函数
-----------------------------------------------------------------------------------------------------------------------------
GET app.get(path,callback)
POST app.get(path,callback)
PUT app.get(path,callback)
DELETE app.get(path,callback)
PATCH app.get(path,callback) 部分更新某个资源
TRACE app.get(path,callback)
CONNECT app.get(path,callback)
OPTIONS app.get(path,callback)
所有方法 app.all(path,callback)-----------------------------------------------------------------------------------------------------------------------------------
app.all函数是将所有请求绑定到同一个响应 函数。
控制权转移
当同时定义多个路由规则时,访问被这些路由规则匹配的路径时,请求总是被迁移条路由规则捕获,后面的规则会被忽略。因为Express在处理路由规则时,会优先匹配定义的路由规则,因此后面相同的规则会被屏蔽掉。
Express提供了路由控制权转移的方法,即回调函数的第三个参数next,通过调用next(),会将路由控制权转移给后面的规则,例如:
app.all('/user/:username',function(req,res,next){console.log('all methods captured');
next();
});
app.get('/user/:username',function(req,res){
res.send('user:',req.params.username);
});
当访问被匹配到的路径时,如http://localhost:3000/user/zjx,会发现终端打印了all methods captured,而浏览器中显示了zjx。说明请求被第一条捕获了完成log后,用next()把转移控制权,又被第二条规则捕获。
从这里我们可以轻易地实现中间件,还能提高代码的复用程度。
例如代码中先使用
app.all拦截所有的请求,判断合法性后在转移控制权,如果不合法直接抛出异常,这样可以很请求的组织结构。方便定义中间件。把相似请求的相同部分提取出来,有利于代码维护其他next方法如果接受了参数,即代表发生了错误。使用这种方法把错误检查分段化,降低代码耦合度。
模板引擎
早期模板引擎存在的问题:
1.页面功能逻辑与页面布局样式耦合,网站规模变大以后逐渐难以维护
2.语法复杂,对于非技术的网页设计者来说,门槛较高,难以学习
3.功能过于全面,页面设计者可以在页面上编程,不利于功能划分,使模板解析效率降低。
现代的模板引擎是MVC的异步分,在功能划分上它严格属于视图部分,因此功能以生成HTML页面为核心,不会引入过度的编程语言的功能。
模板引擎的功能是将页面和要显示的数据结合起来生成HTML页面。模板引擎可以运行在服务器端,生成HTML后传输给客户端。也可以在客户端运行如XSLT,但是由于存在浏览器兼容性问题,不是很流行。目前主要是由服务器运行模板引擎。
页面模板 数据 《-------------- |
| | |
\|/ \|/ 控制器
模板引擎 |
| |
\|/ |
HTML页面------------------------ -》|
使用模板引擎
在app.js中设置:
app.set('views',__dirname+'/views');//页面位置
app.set('view engine','jade');//设置模板引擎
调用模板引擎:
res.render('index', { title: 'Express' });
第一个参数即views目录下的模板文件名,不包含文件扩展名;第二个是要传递给模板的数据,用于模板翻译
//index,jade
extends layout //引用layout
block content//将内容插入到定义的位置
h1= title //定义一个h1表情,标签体为title
p Welcome to #{title} //定义一个p表情,标签体为#{title}引用title参数值
结合layout.jade
//layout.jade
doctype 5
html
head
title= title
link(rel='stylesheet', href='/stylesheets/style.css')
body
block content//定义插入内容的位置
结合两个jade模板可以看出jade,对于没有学过jade的也不是很难看懂。
对于index.jade
完整的内容就是
doctype 5
html
head
title= title
link(rel='stylesheet', href='/stylesheets/style.css')
body
h1= title
p Welcome to #{title}
这样看,就可以对应页面的内容翻译成HTML了。当然也可以关闭引用的layout.jade
如添加代码:
app.set('view options',{layout:false});//不引用layout模板
当然,大多数的网站,有前台展示和后台管理系统,两者的页面结构有很大的区别,一套页面布局不能满足需求。我们可以在模板翻译时指定页面布局,即设置layout属性例如
exports.index = function(req, res){
res.render('index', { title: 'Express admin',layout:'admin' });//调用admin.jade作为页面布局
};