一、简单的静态服务器
1、代码解析
var http = require('http')
// http是nodejs里面的一个模块,这个对象能够提供实现底层的方法。我们通过require去加载这个模块
var server = http.createServer(function(req, res){
// 函数内部创建一个服务器,创建好之后,通过浏览器访问这个服务器的时候,会把请求封装成一个对象
// 这个对象就是这个回调函数的第一个参数req。用户请求的信息都在这个对象内,可以获取用户的信息,如ip,请求信息等。
// 第二个参数res是服务器返回给用户的信息
console.log('jiengu')
res.setHeader("Content-Type","text/html; charset=utf-8")
//设置响应头的content-type内容,text/html是把响应体当成html解析,
res.write('<h1> 饥人谷</h1>')
//在res写入服务器返回给浏览器的内容
res.end()
})
server.listen(9000)
// 通过listen方法来启动他,服务器监听9000端口
2、执行步骤
打开gitbash,切换到js文件当前的文件夹,然后输入node index.js(index.js是我的js文件名,反正你们取什么名就输入啥名)
打开浏览器,输入http://127.0.0.1:9000/,或者http://localhost:9000/
注意哈9000是代码里面写的9000端口,如果下次改成了8080等其他的端口,那就改成对应的端口就好
3、响应头和响应体
响应头查看路径:network-name-headers
响应体:
响应体是response的数据,有点类似于打开网页的查看源代码
每次修改了js文件的内容之后,要断掉git的服务器,重新连接。不然即使刷新网页没有办法显示修改的内容
4、设置响应头
4.1response.setHeader
格式:response.setHeader(name, value)
为一个隐式的响应头设置值。 如果该响应头已存在,则值会被覆盖。 如果要发送多个名称相同的响应头,则使用字符串数组。 非字符串的值会保留原样,所以 response.getHeader() 会返回非字符串的值。 非字符串的值在网络传输时会转换为字符串。
举例:
response.setHeader('Content-Type', 'text/plain'); //当成字符串解析
response.setHeader('Content-Type','text/html; charset=utf-8')//当成html解析,如果是css就设置为text/css
执行结果
setHeader引申的链接,是nodejs中文网的规范
4.2 response.writeHead()
writeHead文档规范
格式:response.writeHead(statusCode, statusMessage)
参数1 statusCode(状态码)是一个三位数的 HTTP 状态码,如 404。
参数2是 statusMessage 是可选的状态描述,是一个string。
参数3 headers 是响应头,是个对象。其实我们可以理解为这个对象放的是response headers全部内容。我们设置的writehead的内容处理status码是放在general,其他的内容都是封装成一个对象放在响应头内容response headers。
response.writeHead(404, 'Not Found')
res.writeHead(200,'hhh', { 'Content-Type':'text/plain;charset=utf-8','X-Foo':'bar2222'});
4.3两者的不同
- response.writeHead() 在消息中只能被调用一次,且必须在 response.end() 被调用之前调用。调用两次就会报错。 setheader可以多次调用
- headers.setheader()只允许您设置单一标题。
writehead()允许您设置关于响应头的几乎所有内容,包括状态代码、内容和多个标题。
4.4遇到的坑
坑1:res.setHeader("Content-Type","text/html; charset=gbk")
才是对的,charset=gbk必须放在Content-Type内部,展示的时候也是在一起。(我猜想charset应该是Content-Type的一部分)
如果分开写成下面的格式,不会报错,但charset就变成了响应头的单独子项展示,而且charset=utf-8不会生效(下图utf-8没有生效就按照gbk去解码,就出现了乱码)。
res.setHeader('Content-Type', 'text/html');
res.setHeader("charset","utf-8")
所以一定注意写法
坑2:writeHead只能写一次,所有响应头要设置的内容都要按照对象的格式,放在参数三headers里面。以下缩写是正确的,要记住啊
res.writeHead(200,'hhh', { 'Content-Type':'text/plain;charset=utf-8','X-Foo':'bar2222'});
坑3:response.setHeader() 设置的响应头会与 response.writeHead() 设置的响应头合并,但是如果设置的内容重复,以response.writeHead() 的优先为准。
var server = http.createServer(function(req, res){
res.setHeader("Content-Type","text/html; charset=utf-8")
res.setHeader('X-Foo', 'bar');
res.writeHead(200,'hhh', { 'Content-Type':'text/plain;charset=utf-8','X-Foo':'bar2222'});
res.write('<h1> 饥人谷2</h1>')
res.end()
})
server.listen(9000)
执行结果是:很明显的看到setHeader和writeHead重复设置的内容,都是以writeHead为准的
4.5设置status的异常
res.writeHead(404,'hhh');
当我设置status为404,发现即使是请求成功回送之后,也会出现红色。这是因为大家约定404就是一个错误的状态,所以status的值要按照约定来设置
二、一个可用的静态服务器
搭建一个有图片,css,js的资源的服务器,github代码链接
1、步骤
- 我在step1文件夹下放置了server.js文件,static文件夹。static文件夹对应放了css,png,js,html等文档,并在html文档内引用了图片,css,js资源。
- 打开gitbash,切换step1文件夹,执行node server.js
- 打开浏览器输入localhost:8080index.html,查看结果
输出内容
2、js代码解析
var http = require('http')
var path = require('path')
// path模块处理url,不同系统(mac/lincx/window)下对url的写法可能不一致的。(一个写成c:/project/code/a.png
// 另外一个可能写成/user/local/project/a.png)。path模块会对这种情况自动处理url类型
var fs = require('fs')
// fs模块用来读取文件数据,也可以往文件里面写数据。
var url = require('url')
// url模块可以自动解析url,得到一个对象,可以获得对应的信息。
function staticRoot(staticPath, req, res){
console.log(staticPath)
//输出static文件的绝对路径,/user/documents/code/node-server/step1/static
console.log(req.url)
//请求的url地址,第一次调用html时,为/index.html,第二次调用css时,就是css/a.css
var pathObj = url.parse(req.url, true)
// 解析url,得到url对象(包含protocal/hostname/port/pathname/query等等),即pathobj对象就是url的对象。本次要用的是pathname
console.log(pathObj)
if(pathObj.pathname === '/'){
pathObj.pathname += 'index.html'
}
//如果pathname没有输入(浏览器输入的值只是localhost:8080,没有后缀的话),服务器默认选择去读取和发送index.html文件
var filePath = path.join(staticPath, pathObj.pathname)
// staticPath=static文件夹的绝对路径, pathObj.pathname=调用文件的后缀地址。
// 两个加起来得到filePath(用户输入的url想要访问文件的绝对路径),举例本文是/user/documents/code/node-server/step1/static/index.html
// var fileContent = fs.readFileSync(filePath,'binary')
// res.write(fileContent, 'binary')
// // 采用同步的方式读取filePath的文档,把读取的数据写入res对象内
// res.end()
fs.readFile(filePath, 'binary', function(err, fileContent){
// 异步的方式来读取filePath的文档。binary指以二进制的方式来读取数据,因为服务器不仅仅要读取普通的数据,需要兼容图片和文件等数据。
if(err){
console.log('404')
res.writeHead(404, 'not found')
res.end('<h1>404 Not Found</h1>')
// 在页面展示404 Not Found。在res.end('数据')等于执行res.write('数据')加上res.end()
}else{
console.log('ok')
res.writeHead(200, 'OK')
res.write(fileContent, 'binary')
// 通过二进制的方式发送数据
res.end()
}
})
}
console.log(path.join(__dirname, 'static'))
// 在浏览器输入localhost:8080/index.html地址,浏览器向服务器发起请求。
// 服务器收到请求后,执行相关函数,解析req对象信息,得到了index.html的地址。
// 服务器根据解析的地址在本地static文件夹下找到对应的index.html文件,读取html里面数据,并把数据放在res内,当成字符串发给服务器。
var server = http.createServer(function(req, res){
staticRoot(path.join(__dirname, 'static'), req, res) //写一个staticRoot函数,来处理请求。
/* 参数1:把哪个路径当成静态文件路径,传递路径名。__dirname是nodejs里面的一个变量,代表当前的server.js执行的这个文件。
path.join(__dirname, 'static')可以使用一个或多个字符串值参数,该参数返回将这些字符串值参数结合而成的路径。
var joinPath = path.join(__dirname, 'a', 'b', 'c');
console.log(joinPath); // D:\nodePro\fileTest\a\b\c,
__dirname对应的step1文件夹的路径,加上static文件夹得路径,就等于static的绝对路径。、
这样的好处是每次绝对路径发生变化的时候,不用重新去修改绝对路径。*/
})
server.listen(8080) //创建一个服务器,监听8080端口
console.log('visit http://localhost:8080' )
3、代码难点解析
3.1 path node.js文档中的标准解释
path 模块用于处理文件与目录的路径。不同系统(mac/lincx/window)下对url的写法可能不一致的。(一个写成c:/project/code/a.png
// 另外一个可能写成/user/local/project/a.png)。path模块会对这种情况自动处理url类型
3.2 path.join([...paths])
参数...paths <string> :路径片段的序列,返回: <string>
使用平台特定的分隔符把所有 path 片段连接到一起,并规范化生成的路径
path.join('C:\Users\jz\documents\code\node-server\step1'
, 'static')
//C:\Users\jz\documents\code\node-server\step1\static
3.3 fs 文件系统node.js文档中的标准解释
fs 模块用于以一种类似标准 POSIX 函数的方式与文件系统进行交互。
所有的文件系统操作都有同步和异步两种形式。
异步形式的最后一个参数是完成时的回调函数。 传给回调函数的参数取决于具体方法,但第一个参数会保留给异常。 如果操作成功完成,则第一个参数会是 null 或 undefined。
3.4 fs.readFile(path[, options], callback)异步地读取文件的内容
path 文件名或文件路径
options 如果 options 是一个字符串,则指定字符编码,默认为 null
callback 是一个回调函数,有两个参数 (err, data),其中 data 是要读取文件的内容
fs.readFile(filePath, 'binary', function(err, fileContent){
// 异步的方式来读取filePath的文档。binary指以二进制的方式来读取数据,因为服务器不仅仅要读取普通的数据,需要兼容图片和文件等数据。
if(err){
console.log('404')
res.writeHead(404, 'not found')
res.end('<h1>404 Not Found</h1>')
// 在页面展示404 Not Found。在res.end('数据')等于执行res.write('数据')加上res.end()
}else{
console.log('ok')
res.writeHead(200, 'OK')
res.write(fileContent, 'binary')
// 通过二进制的方式发送数据
res.end()
}
})
3.5 fs.readFileSync(path[, options])
同步的读取文件内容,两个参数和异步的一样的用法
// var fileContent = fs.readFileSync(filePath,'binary')
// res.write(fileContent, 'binary')
// // 采用同步的方式读取filePath的文档,把读取的数据写入res对象内
// res.end()
3.6 url模块node.js文档中的标准解释
url 模块提供了一些实用函数,用于 URL 处理与解析。 URL 字符串可以被解析为一个 URL 对象,其属性对应于字符串的各组成部分。
3.7url.parse(urlString[, parseQueryString[, slashesDenoteHost]])
url.parse() 方法会解析一个 URL 字符串并返回一个 URL 对象。
urlString <string>
要解析的 URL 字符串。
parseQueryString <boolean>
如果为 true,则 query 属性总会通过 querystring 模块的 parse() 方法生成一个对象。 如果为 false,则返回的 URL 对象上的 query 属性会是一个未解析、未解码的字符串。 默认为 false。
slashesDenoteHost <boolean>
如果为 true,则 // 之后至下一个 / 之前的字符串会被解析作为 host。 例如,//foo/bar 会被解析为 {host: 'foo', pathname: '/bar'} 而不是 {pathname: '//foo/bar'}。 默认为 false。
举个例子
var pathObj = url.parse(req.url, true)// 解析req.url,得到url对象pathobj
3.8__dirname
当前模块的文件夹名称。等同于 __filename 的 path.dirname() 的值
__filename 当前模块的文件名称---解析后的绝对路径
例如:
在 /Users/mjr 目录下执行 node example.js
console.log(__filename);
// Prints: /Users/mjr/example.js
console.log(__dirname);
// Prints: /Users/mjr
4、坑
有一个问题,为什么我们要用req.url解析成url对象pathobj,再通过staticPath文件地址和pathobj.pastname结合成filepath,为啥我们不直接把req.url和staticPath结合在一起生成filepath呢?这样还少了一步呢
答案:如果requrl是常规的index.html或者css.css这种,两种方式都不会报错。但是如果url比较复杂,像是index.html?query=111#111这种,直接把req.url和staticPath结合在一起是会报错的,所以需要转成url对象再把pashname挑出来。
三、实现一个简单的node.js服务器路由
实现更复杂的服务器,url不仅仅是定位一个静态文件,可以mock任何数据和前端交互。
1、核心原理:
根据浏览器请求的不同路由,导致服务器执行不同的操作。
2、文档结构:
3、服务器实现3条路由:
- /getWeather,结合b.js文件实现一个ajax来mock天气数据
- /user/123 ,结合user.tpl文件实现用户页面
- /index.html,结合index.html实现index.html的页面。在html引用css文件,b.js,和图片
4、对应的文件内容
可以查看GitHub上面的代码,我这里截图说明
html
css
js,实现ajax的代码
user.tpl
最重要的server-simple.js服务器代码
本次演示的url是localhost:8080/user/123,localhost:8080之后的内容是路由。所有请求到8080这个服务器内,根据不同的路由给浏览器发送不同的数据
var http = require('http')
var fs = require('fs')
var url = require('url')
http.createServer(function(req, res){
var pathObj = url.parse(req.url, true)
console.log(pathObj)
switch (pathObj.pathname){
case '/getWeather': //根据req.url来执行不同的函数
var ret
if(pathObj.query.city == 'beijing'){
ret = {
city: 'beijing',
weather: '晴天'
}
}else{
ret = {
city: pathObj.query.city,
weather: '不知道'
}
}
res.setHeader('content-Type','text/plain;charset=utf-8')
res.end(JSON.stringify(ret)) //给浏览器输入是一个json格式的对象,根据JSON.stringify转换成字符串
break;
case '/user/123':
res.end( fs.readFileSync(__dirname + '/static/user.tpl' ))
//如果路由是/user/123,读取user.tpl的内容,并返回给浏览器
break;
default:
res.end( fs.readFileSync(__dirname + '/static' + pathObj.pathname) )
}
}).listen(8080)
5、执行结果
index.html
/getWeather
/user/123
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。