eg.一个简单的图片托管网站
服务器端
ar fs = require('fs')
var http=require('http')
var qs=require('querystring')
var server=http.createServer(function(req,res){
function serve(path,type){
res.writeHead(200,type)
fs.createReadStream(path).pipe(res)
}
if(req.method=="GET"&&req.url=="/"){
serve(__dirname+'/index.html','text/html')
}else if(req.method=="GET"&&req.url.substr(0,7)=="/images"&&req.url.substr(-4)==".jpg"){
fs.stat(__dirname+req.url,function(err,stat){
if(err||!stat.isFile()){
res.writeHead(404)
res.end("404 NOT FOUND")
return
}
serve(__dirname+req.url,'application/jpg')
})
}else{
res.writeHead(404)
res.end("404 NOT FOUND")
}
})
server.listen(3000)
index.js
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>website-test</title>
</head>
<body>
<h1>my website</h1>
<image src="/images/1.jpg"></image>
<image src="/images/2.jpg"></image>
<image src="/images/3.jpg"></image>
<image src="/images/4.jpg"></image>
</body>
</html>
下面我们可以通过中间件来实现服务器逻辑
var connect=require('connect')
//var http=require('http')
var timeout = require('connect-timeout')
var morgan = require('morgan')
var serveStatic = require('serve-static')
var server=module.exports = connect()
.use(timeout('1s'))
.use(morgan('dev'))
.use(serveStatic(__dirname+'/website'))
.use(function(req,res,next){
console.log("%s %s",req.method,req.url)
next()
})
server.use(function(req,res,next){
// function serve(path,type){
// res.writeHead(200,type)
// fs.createReadStream(path).pipe(res)
// }
if(req.method=="GET"&&req.url.substr(0,7)=="/images"){
//serve(__dirname+req.url,'application/jpg')
}else{
next()
}
})
server.use(function(req,res,next){
// function serve(path,type){
// res.writeHead(200,type)
// fs.createReadStream(path).pipe(res)
// }
if(req.method=="GET"&&req.url=="/"){
//server.use(serveStatic(__dirname+'/website'+req.url))
}else{
next()
}
})
server.use(function(req,res,next){
res.write(404)
res.end('404 NOT FOUND')
})
server.listen(3000)
一、npm包常用api查找
npm官网:www.npmjs.com/
原生api:nodejs.cn/
在学习《了不起的Node.js》一书时,发现它使用的connect包版本十分老旧,导致了一些中间件已经无法调用,我们可以通过文档来找到connect常用的中间件,以方便我们的学习。
这里列出了一些Connect用到的中间件:www.npmjs.com/package/con…
二、Connect
什么是中间件?
可以组织代码来与请求、响应对象进行交互的工具
Pre-Request 通常用来改写request的原始数据;
Request/Response 大部分中间件都在这里,功能各异;
Post-Response 全局异常处理,改写response数据等;
之后会提到部分中间件以及他们的作用。
Connect是一个优秀的中间件集成平台,可以通过中间件复用很多代码。同时如上面的代码所示,Connect采用的是一个中间队列,流式处理,中间件可以自由组合和插拔。
1.流式处理
var server=connect()
.use(timeout('1s'))
.use(morgan('dev'))
.use(serveStatic(__dirname+'/website'))
.use(function(req,res,next){
console.log("%s %s",req.method,req.url)
next()
})
2.原型
connect的核心代码,即维护了一个中间件队列,请求来临时调用中间件
app.stack = [];
app.use = function(route, fn) {
// ...
// add the middleware
debug('use %s %s', route || '/', fn.name || 'an onymous');
this.stack.push({
route: route,
handle: fn
});
return this;
};
function (req, res, next) {
// 中间件
}
如图所示
我们可以用原型以此处理请求并判断类型,可以使代码逻辑更加清晰。
3.静态资源托管
通过中间件 serve-static可以实现,正如我们的例子中的图片一样,只要将其资源的文件夹托管,下属的各类资源会被中间件接管,可以准确的找到所需资源,这里大大的减少了冗余的代码量。比如我们第一次实现时写的serve函数就可以被简省掉了。
var serveStatic = require('serve-static')
server.use(serveStatic(__dirname+'/website'))
server.use(function(req,res,next){
// function serve(path,type){
// res.writeHead(200,type)
// fs.createReadStream(path).pipe(res)
// }
if(req.method=="GET"&&req.url.substr(0,7)=="/images"){
//serve(__dirname+req.url,'application/jpg')
}else{
next()
}
})
server.use(function(req,res,next){
// function serve(path,type){
// res.writeHead(200,type)
// fs.createReadStream(path).pipe(res)
// }
if(req.method=="GET"&&req.url=="/"){
//server.use(serveStatic(__dirname+'/website'+req.url))
}else{
next()
}
})
maxAge
server.use(serveStatic(__dirname+'/website'+req.url,{maxAge:1000000})
可以设置缓存时间,对于一个不经常动用的资源来说,就可以减少它的请求次数
hideen
可以托管那些在UNIX下被隐藏的文件
logger日志记录
var morgan = require('morgan')
server.use(morgan('dev'))
C:\Users\15905\Documents\code\my-website>node index
GET / 200 6.823 ms - 461
GET /images/1.jpg 304 3.951 ms - -
GET /images/2.jpg 304 4.527 ms - -
GET /images/3.jpg 304 6.115 ms - -
GET /images/4.jpg 304 3.681 ms - -
timeout超时记录
var timeout = require('connect-timeout')
server.user(timeout('1s'))
书上给出了手动实现的方法,超过所设置的时间就输出信息,或者在响应完清除定时器。
module.exports=function(opts){
var time=opts.time||100
return function(req,res,next){
var timer=setTimeout(function(){
console.log("%s %s is taking too much time",req.method,req.url)
},time)
}
var end=res.end
res.end=function(chunk,encoding){
res.end=end
res.end(chunk,encoding)
clearTimeout(timer)
}
next()
}
query
查询并获取url头中的一些数据,并可以去设置
server.use(connect.query)
server.use(function(req,res){
req.query.use=="5
}
bodyparser
使用这个的时候遇到了一点问题。书上用这个包处理了form-data类型的请求头的数据,但是现在这个方法好像已经被弃置了。所以我想了两个解决方法,第一个方法是queryString,发现这个现在只能处理application/www-encoded类型的数据,也就是默认的请求头,对于form-data并不能解析。第二个方法是使用其他的包,比如connect-multiparty来解决。 服务端代码
var connect=require('connect')
var serveStatic = require('serve-static')
var fs=require('fs')
var server=connect()
var multipart = require('connect-multiparty');
server.use(serveStatic(__dirname+'/static'))
server.use(function(req,res,next){
if(req.method=="GET"&&req.url=="/"){
//console.log('get html')
}else{
next()
}
})
var multipartMiddleware = multipart();
server.use(multipartMiddleware)
server.use(function(req,res,next){
var post=''
if(req.method=="POST"&&req.url=="/"){
console.log('serve receive file')
console.log(req.body,req.files)
res.writeHead(200)
res.end("ok")
}
else{
next()
}
})
server.listen(3000,function(){
console.log("the server is on ")
})
HTML
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<form action="/" method="POST" enctype="multipart/form-data">
<input type="file" name="file"/>
<input type="text" name="username"/>
<input type="submit" name="submit" value="发送">
</form>
</body>
</html>
输出
{ username: 'eee', submit: '发送' } {
file: {
fieldName: 'file',
originalFilename: 'ZIKU.circ',
path: 'C:\\Users\\15905\\AppData\\Local\\Temp\\jUyCLNJ2HO2keFIOvsJixv5R.circ',
headers: {
'content-disposition': 'form-data; name="file"; filename="ZIKU.circ"',
'content-type': 'application/octet-stream'
},
size: 590082,
name: 'ZIKU.circ',
type: 'application/octet-stream'
}
}
cookieParser
设置cookie的两种方式,第一种直接用res.writeHead()添加在头上,为请求头添加键对值{'setCookie':[]},第二种可以用cookieParser中间件添加(connect中res.cookie好像没法添加)。cookieParse还可以解析当前请求携带的cookie值
第一种方式
var cookieParser=require('cookie-parser')
var connect=require('connect')
var app=connect()
app.use(cookieParser())
app.use(function(req,res,next){
if(req.url=="/set"&&req.method=="GET"){
res.writeHead(200,{
'Set-Cookie': ["aaa=bbb","ccc=ddd","eee=fff"],
})
res.end("ok")
}
else next()
})
app.use(function(req,res,next){
if(req.url=="/"&&req.method=="GET"){
// Cookies that have not been s igned
console.log('Cookies: ', req.cookies)
// Cookies that have been signed
console.log('Signed Cookies: ', req.signedCookies)
res.writeHead(200)
res.end("ok")
}
else next()
})
.listen(3000,function(){
console.log('listening on 3000')
})