闲来无事,不如撸码
Express现在异常流行,尤其是近两年。笔者现在上网找资料能看到好多“express实现…”、“基于express的…”实战型文章。作为一个简洁而灵活的node.js Web应用框架,express提供了一系列强大特性帮助创建各种web应用。
express能做的事情有很多。。。
Express与Connect
说到express就不得不提到connect,其一是因为它们出自于同一个作者,另一个原因是express中利用了connect中间件框架,整合了一些适用于web应用中需要的东西,比如:
- 强大的路由
- 视图系统
- 基于环境的配置
- 持久会话
- 内容协商
- 响应工具
- 重定向工具
- 能快速生成应用骨架的功能
如何安装express
首先,在cmd(或power shell)中切换到指定目录;
然后,运行命令
npm install -g express
如果安装的是express4以后的版本,则使用命令:npm install -g express-generator
(-generator安装的是express生成器,express生成器和express不是一个概念 —— 虽然在express4以前两者被统一在一起,但从4版本开始,两者分开了,我们安装的通常是生成器)
用express搭建简单web应用
如果前面用的是npm install -g express-generator
命令,安装完成后,键入命令:
express -e mxcyun
就会创建一个名为mxcyun的web应用项目的文件骨架,但这里还未完工 —— 一些项目依赖的组件还没有安装完成。
这时,我们还需cd到mxcyun目录中,进行命令:
npm install
至此,项目搭建完成,我们可通过命令:
npm start
来启动项目(在mxcyun目录下)
express创建的默认文件结构
文件/目录 | 说明 |
---|---|
package.json | npm依赖配置文件,类似ruby中的Gemfile、java Maven中的pom.xml |
app.js | 项目的入口文件,类似index.php |
/public/ | 静态文件目录 |
/views/ | 视图(模板)程序文件目录 |
/routes/ | 路由控制器程序文件目录 |
express的程序控制
express提供了一系列用于搭建web应用程序的API:
模板引擎ejs:
打开mxcyun项目根目录的app.js文件,我们可以看到它简洁的代码。
其中app.set('views engine','ejs');
就是设置默认的模板解析引擎。传统开发语言,如ASP、PHP、JSP等,都没有附带模板解析功能,很多都是由第三方开发;而express不仅内置了模板解析,还提供了多种模板引擎,这是很多网页开发语言无法媲美的优势。
express默认使用ejs后缀的模板文件,模板目录在views文件夹下。一般来说,网页设计师或前端工程师输出的文件基本上都是html后缀格式的文件,为了减少转换,只需配置一下即可让express识别html格式的模板文件:
app.set('views',__dirname+'/views'); //设置渲染的模板视图-一般放在语法糖配置项app.configure()中
app.set('view engine','html'); //设置为HTML后缀
app.engine('.html',require('ejs').__express); //引擎依然为ejs
app.set('view engine','z3f'); //设置为自定义文件后缀
app.engine('.z3f',require('ejs').__express);
在这样的情况下,前端模板渲染是根据服务器(后端)传入的文字:
//routes.index -> /routes/index.js
var express=require('express');
var router=express.Router();
router.get('/',function(req,res,next){
res.render('index',{title:'Express'})
});
module.exports=router;
这相当于数据,对应的模板文件为/views/index.ejs
,代码如下:
<!DOCTYPE html>
<html>
<head>
<title><%= title %></title>
</head>
<body>
<p>welcome to <%= title %></p>
</body>
</html>
中间件middleware
中间件好像一直是业界推崇的一个点。中间件向流水线一样,对来访的请求进行层层处理,处理完后又交给下一环节,如此往复。
笔者曾见过老师写的中间件,上面处理完请求下面处理报错,然后处理数据,数据处理完再交给router,中间件封装合理有序,整个过程异常好看(额…这里用“好看”似乎不妥,权当做心情吧哈哈)。就像是java中的过滤器一样。
在程序代码中,哪些是中间件呢?
一个简单的标识就是app.use()
,通过这个方法调用的大多都是中间件。
比如:我们要禁止某些IP不能访问本站,我们可以封装这样一个中间件:
module.exports=function(req,res,next){
var ips=['127.0.0.1','192.168.0.52'];
if(ips.indexOf(req.connection.remoteAddress)>-1){
res.send('stop ! ')
}else{
next();
}
};
对了,中间件必有的一样就是next()
(因为它只是整个流程中的“一环”),这也可以算作标识之一吧。
上面代码逻辑很简单:如果客户端IP在“黑名单”(ips)里,则通过res.end()
种植程序运行,否则用next()
继续到下一个中间件!
在app.js中调用:
var bannedIP=require('./middleware/bannedIP');
//...
app.use(bannedIP);
express请求解析
路由routes
路由习惯上也被称为URL映射,基本上都是对URL的解析。
URL是跟随http一起的,就像门牌号一样重要,一个清晰的URL路径甚至对于SEO优化也是非常重要的。
在express中主要用get、post、all这三种方法来处理路由的操作
express提供的路有格式主要有两种:一是纯字符串路由;二是正则表达式路由
app.get('/',routes.index);
app.get('/users',user.list);
app.get(/^\/page\/(\w+)(?:-(\w+))?$/,function(req,res){
var from=req.params[0];
var to=req.params[1] || from;
console.log('page: '+from + '-'+to);
});
app.get('/:myvar/:myvar2',function(req,res){
console.log('myvar: '+req.params.myvar+'<br />'myvar2: '+req.params.myvar2);
});
注意:每次修改完app.js必须重启服务器
上面代码中的req.params
就是node中Request对象锁提供的API,其余还有比如:
方法或属性 | 说明 |
---|---|
req.params | 数组对象,命名过的参数会以键值对的形式存放 |
req.query | 一个解析过的请求参数对象 |
req.body | 对应的是解析过的请求表单,由bodyParser()中间件提供(具体见这篇文章) |
req.files | 上传的文件对象,由bodyParser()中间件提供(具体见这篇文章) |
req.param(name) | 返回name参数的值 |
req.route | 当前匹配的路由里包含的属性 |
req.cookies | Cookie对象,由cookieParser()中间件提供 |
req.is(type) | 检查头文件里是不是包含Content-Type字段 |
req.path | 返回请求的URL的路径名 |
req.host | 返回从Host请求头里获取的主机名,不包含端口号 |
req.fresh | 判断请求是不是新的 |
req.xhr | 判断请求头里是否有X-Requested-With这样的字段并且值为XMLHttpRequest |
req.ip | 返回客户端地址 |
而Response对象——express的响应控制,也有自己的API,常用的有:
方法或属性 | 说明 |
---|---|
res.status | 返回指定响应状态 |
res.cookie(name,value,[options]) | 设置cookie name值为value,也接受一个对象设置多个值 |
res.clearCookie(name,[options]) | 清除指定cookie |
res.redirect([status],url) | 使用可选的状态码跳转到url,状态码默认为302-Found |
res.charset | 设置字符集,默认为utf-8 |
res.send() | 发送一个响应 |
res.json() | 返回一个json响应 |
res.type(type) | 设置Content-Type |
res.sendfile(path,[options],[fn]) | 传输指定path文件,根据扩展名自动设置响应头 |
res.download(path,[filename],[fn]) | 把path当做附件传输,通常浏览器会弹出一个下载文件的窗口,可回调 |
res.render(view,[locals],callback) | 渲染view,同时向callback传入渲染后的字符串 |
有关write、send、end输出响应到客户端
若是要服务器向客户端发送信息——让客户端看到,就需要前面说的Response对象“大显身手”了。
和大多数web编程语言一样,write()
方法几乎是最常用的 —— 比如打印helloworld:
app.get('/hello',function(req,res){
res.write('Helloworld');
res.end('end');
});
end()
方法也有这个功能,但是它最常用于“结束程序,开始显示”,即到这里时服务器会把所有前面准备好的响应信息发送给用户和浏览器显示。
和end()方法类似的还有send()
方法 —— 他们一样都能接收响应字符串,而他们和write()不同之处在于:他们返回的数据一般要由前端js继续处理再显示
而且,send()方法自动补足了一些响应头信息,如Connection、Content-Type、Date等等。
但是需要注意的是:在一个路由处理中,如果使用了send()方法,那么在send()之前的代码中出现write()方法则会报错!
(当然,write()输出的内容还是会发送到客户端去,而且服务器端控制台会报500错,浏览器客户端则依然是200状态!)
设置cookie
对于一个网站来说,cookie是必不可少的,所以express也对cookie进行了封装处理:
我们可以进行cookie的读写
//写入一个cookie,设置100s后过期
res.cookie('user','z3f',{maxAge:100000});
//在控制台打印cookie
console.log(req.cookie)
也可以使用cookies中间件处理json格式的cookie
app.use(express.cookieParser());
…
有关cookie的操作还有许多,日后再与大家讨论。