hello,JS:12-03搭建服务器(下)

(一)实例一:写一个简单的服务器

一、nodejs写服务器

1、下载github代码

2、开始操作:

(1)分析项目文件step0中的文件index.js,代码如下:

1
var http = require('http')//是nodejs的内置模块——http(服务器)

即nodejs通过require加载一个模块(一个对象,提供了一些方法能够实现所需要的功能),服务器的底层是由这个nodejs的http模块实现

(2)回到项目文件,用nodejs的内置模块创建一个server:

1
2
3
http.createServer(function(req , res){

})//内含一个回调函数

以上函数实质上内部是一个异步过程,内部可以创建一个服务器,以这个函数作为对应的参数去处理请求。

当浏览器去访问这个服务器的时候,该请求底层会被封装成一个对象。

  • 一个对象:参数req(随意取名),即用户请求的信息都存在这个req对象中,通过获取req得到相关的信息数据(如用户IP、域名,以及一些浏览器的域名等等)
  • 另一个对象:参数res,即需要返回给用户哪些东西

(3)最终在项目文件中创建一个有着基本设置的服务器。代码如下:

1
2
3
4
5
6
7
8
9
//index.js

var http = require('http')
var server = http.createServer(function(req, res){
console.log(req) //用户请求所附带的信息
res.write('hello world') //返回浏览器自带的一些信息
res.end()
})
server.listen(9000) //用listen启动这个静态服务器

(4)终端启动:

1
$ node index.js

此时终端进入一个空白状态(输入无用),即服务器处于一个启动状态,等待用户输入

(5)浏览器输入服务器地址:localhost:9000 (地址不一定一样,端口号自己弄)。出现如图:

同时,终端相应出现req这个对象所附带的用户请求的信息(随意截图)

注: 事实上,console.log所执行的请求数据,除了可以是设定好的参数req,也可以是任意字符串,或者包含数据的json数据等等(不演示了)

至此,写了一个简单的服务器

延伸:解析一下响应体如何进入服务器——服务器处理请求——返回数据:
res.write('hello world')
举例:res.write(即一个响应体)是把数据(即'helloworld')放到http的response响应体里(即响应内容里)
当我们代码请求后输入url发出网络请求,请求时浏览器自动添加响应头相关信息,请求发至服务器之后,则会开始执行以下代码:【这里就是开发者开发不同页面的个性创造所在】

1
2
3
4
5
var server = http.createServer(function(req, res){
console.log(req) //用户请求所附带的信息
res.write('hello world') //返回浏览器带的信息(即响应体,页面展现的内容)
res.end()
})

该服务器发了一个响应res(response),此时服务器后台出现响应头(一些浏览器默认参数),如图:
响应头

响应体对应代码中,则res所请求的内容,这里是'hello world'
响应体

总结,这是网站后台的一个基本逻辑

3、扩展:设置响应头

通过res.setHeader()设置响应头,如可添加:

1
2
3
4
res.setHeader("Content-Type","text/plain; charset=utf-8")

//text/plain 表示返回内容用字符串(明文)去呈现或者当成html渲染,如text/html
//charset=gbk 表示返回的内容用gbk解码,也可设置为charset=utf-8解码

即项目文件代码为:

1
2
3
4
5
6
7
8
9
10
//index.js

var http = require('http')
var server = http.createServer(function(req, res){
console.log('jiengu')//服务器响应的内容
res.setHeader("Content-Type","text/plain; charset=utf-8")
res.write('你好世界')//响应体:页面展现的内容
res.end()
})
server.listen(9000)

重启终端:

1
$ node index.js

服务器后台响应头设置相应参数后,出现如图:

注意: 如出现乱码文件,实质可以在服务器的响应头层面去设置编码方式。如果返回的是一个html则可以从meta里去设置

延伸:ajax的请求头 VS nodejs的响应头
miya Wang:#hello,JS:12-01技术方案:Ajax 使用和原理

  • ajax请求头:即服务器发送、返回时带的一些资源数据,如将url包含的一些各类数据作为请求资源,做一些加载、拼接等,然后返回结果数据。
  • nodejs的响应头:服务器对所请求的东西后台参数的一些响应,如设置页面展示效果的展示

4、加一个定时器观察服务器的响应

项目文件代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
//项目文件:(新)index.js

var http = require('http')

var server = http.createServer(function(request, response){
setTimeout(function(){

response.setHeader('Content-Type','text/html; charset=utf-8')
//响应头:论及权限高,charset=utf-8作为请求http(浏览器)级别的请求,去解码
//发一个请求去响应:头、身体内容,根据头解释身体内容(一堆字符串)

response.writeHead(404, 'Not Found')
response.write('<html><head><meta charset="gbk" /></head>')
response.write('<body>')
response.write('<h1>你好</h1>')
response.write('</body>')
response.write('</html>')

response.end()
},2000);
})

console.log('open http://localhost:8080')
server.listen(8080)

重新启动终端:

1
$ node index.js

检查——控制台 ,延伸:针对http对应的状态码作用。先看到几个效果:

  • 当Status Code:200时,如图:
  • 当Status Code:400时,如图:

    页面打开了(页面出现:你好)请求是成功的,但是打开控制台显示请求为红色,且console出现报错:404(Notfound)。一旦浏览器收到状态码为404,它则认为文件请求失败,但实际上它也是收到请求并返回数据的。

总结: 所请求的状态码通过response.writeHead()写出,完全由你来决定其页面呈现的状态

(二)实例二:实现一个静态服务器

准备好打包好的项目文件夹(包含html(css样式/js交互/图片))——网站放置在远程服务器上——通过执行服务器的相关js文件(执行node server.js)——运行服务器

那么,这个运行服务器的js文件到底是怎么实现访问页面?
分析项目文件step1中的文件关键js文件:server.js,代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
//server.js

var http = require('http') //前面说了,创建http服务器的底层内置模块
var path = require('path') //该模块可以处理不同系统下的url(不同系统路径写法不一)
var fs = require('fs') //该模块读、写文件
var url = require('url') //该模块自动解析url,读取信息,如控制台求location

function staticRoot(staticPath, req, res){
console.log(staticPath)

var pathObj = url.parse(req.url, true)
console.log(pathObj)


if(pathObj.pathname === '/'){
pathObj.pathname += 'index.html'
}

var filePath = path.join(staticPath, pathObj.pathname)

// var fileContent = fs.readFileSync(filePath,'binary')
// res.write(fileContent, 'binary')
// res.end()


fs.readFile(filePath, 'binary', function(err, fileContent){
if(err){
console.log('404')
res.writeHead(404, 'not found')
res.end('<h1>404 Not Found</h1>')
}else{
console.log('ok')
res.writeHead(200, 'OK')
res.write(fileContent, 'binary')
res.end()
}
})

}

console.log(path.join(__dirname, 'static'))

var server = http.createServer(function(req, res){
staticRoot(path.join(__dirname, 'static'), req, res)
})

server.listen(8080)
console.log('visit http://localhost:8080' )

当用户访问localhost:8080/index.html,如何让用户看到项目文件的内容呢?
实现的关键,分解来看:

1
2
3
4
5
6
7
8
9
10
11
var http = require('http') 
var path = require('path')
var fs = require('fs')
var url = require('url')

var server = http.createServer(function(req, res){
staticRoot(path.join(__dirname, 'static'), req, res)
})

server.listen(8080)
console.log('visit http://localhost:8080' )

通过http.creatServer创建一个server,listen去启动一个服务器,监听8080端口,请求到来之后,进入server这个函数里,处理这个请求。写一个函数staticRoot()作为静态文件路径,将路径名、req、res都传递进去,如:

1
staticRoot(path.join(__dirname, 'static'), req, res)

__dirname 为nodejs的内置变量,代表当前的文件server.js 再加上【static】,那么,path.join(__dirname, 'static') 则会生成一个 绝对路径,然后通过下面代码运行测试:

1
2
3
function staticRoot(staticPath, req, res){
console.log(staticPath)
}

控制台随即得到项目文件的相关文件信息,通过绝对路径能读取文件。获取路径之后进行操作,需要通过用户的url,给用户返回一些特定内容,发出请求得到返回的是console.log(req.url)req.url所返回的均是项目文件中的相关文件,即请求的均是这些文件index.html、a.css、logo.png,并得到,得到之后进行解析,如何解析?代码如下:

1
2
var pathObj = url.parse(req.url, true)
console.log(pathObj)

实现一个默认页面:设置一个默认路径:localhost:8080/index.html ,如何实现?代码如下:

1
2
3
if(pathObj.pathname === '/'){
pathObj.pathname += 'index.html'
}

发现原来可以这样设置一个默认路径的页面,也是很兴奋哦~:

通过一个parseName去得到一个完整路径,即静态目录路径则进入打包项目文件夹里,那么加上pathObj.pathname就得到该项目文件所请求的绝对路径地址filePath,代码如下:

1
var filePath = path.join(staticPath, pathObj.pathname)

如何读取文件?代码如下
第1种:直接法

1
2
3
var fileContent = fs.readFileSync(filePath,'binary')
res.write(fileContent, 'binary')
res.end()

第2种:异步(也可作为当做制作404的页面状态)

1
2
3
4
5
6
7
8
9
10
11
12
fs.readFile(filePath, 'binary', function(err, fileContent){
if(err){
console.log('404')
res.writeHead(404, 'not found')
res.end('<h1>404 Not Found</h1>')
}else{
console.log('ok')
res.writeHead(200, 'OK')
res.write(fileContent, 'binary')
res.end()
}
})

?处理关于乱码的事情

(三)实例三:实现功能更复杂的静态服务器(使用nodejs服务器理由解析)

如url可获取任何数据,mock数据与前端交互。分析项目文件step2中的文件server-simple.js,代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
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': //请求对应天气
var ret
if(pathObj.query.city == 'beijing'){
ret = {
city: 'beijing',
weather: '晴天'
}
}else{
ret = {
city: pathObj.query.city,
weather: '不知道'
}
}
res.end(JSON.stringify(ret))
break;
case '/user/123': //对应的路由
res.end( fs.readFileSync(__dirname + '/static/user.tpl' ))
break;
default:
res.end( fs.readFileSync(__dirname + '/static' + pathObj.pathname) )
}
}).listen(8080)

先分解简单代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
var http = require('http')
var fs = require('fs')

//根据函数req.url进行语句操作,假设req.url是请求getWeather,
//res.end()括号内则是请求后所要获取的结果。
http.createServer(function(req, res){
switch (req.url){
case '/getWeather':
res.end(JSON.stringify({a:1,b:2}))
break;
case '/user/123':
res.end( fs.readFileSync(__dirname + '/static/user'))
break;
default:
res.end( fs.readFileSync(__dirname + '/static'+req.url))
}
}).listen(8080)

总结: 创建模块:http+fs模块——创建一个server——该server函数返回一个操作对象——该操作对象调用并启动端口为8080的服务器。当任何请求到来,只要以localhost:8080为前缀的url就会到达该服务器

终端启动:

1
$ node server-simple.js

刷新页面处理js文件中函数对象的请求,即

1
2
3
4
5
6
7
8
9
10
11
12
http.createServer(function(req, res){
switch (req.url){
case '/getWeather':
res.end(JSON.stringify({a:1,b:2}))
break;
case '/user/123':
res.end( fs.readFileSync(__dirname + '/static/user'))
break;
default: //表示用户希望请求的是一个静态文件,直接从static文件夹中读取文件req.url
res.end( fs.readFileSync(__dirname + '/static'+req.url))
}
}).listen(8080)

当我们访问static下的html、css、图片或者直接调取请求模块,都能获取相应的内容

一个复杂网站,具有静态功能,提供静态文件;可以处理动态路由;可以mock数据,通过以下代码启示:

1
2
3
4
5
6
7
8
var http = require('http')
http.createServer(function(req, res){
switch (req.url){
case '/getWeather':
res.end(JSON.stringify({a:1,b:2}))
break;
}
}).listen(8080)

可以写一个ajax:

1
2
3
4
5
6
7
8
//b.js

var xhr = new XMLHttpRequest()
xhr.open('GET', '/getWeather?city=hangzhou', true)
xhr.send()
xhr.onload = function(){
console.log(JSON.parse(xhr.responseText))
}

待续…

看nodejs这一节最后的时候,不知道是有点困逻辑无法梳理,还有一点复杂服务器的实现暂时看得不是很懂😂。并且对于nodejs的一些理论基础知识也是相对空白,看了阿里出品的《七天学会NodeJS》才发现原来老师是将理论融进这短短的几节课,然后就理解了很多理论的知识。

-------------本文结束感谢您的阅读-------------