一、简单的静态服务器1、代码分析varhttp=require('http')//http是nodejs中的一个模块,这个对象可以提供底层方法。我们使用require来加载这个模块varserver=http.createServer(function(req,res){//在函数内部创建一个服务器,创建之后,通过浏览器访问这个服务器时,请求会被封装成一个对象//这个对象就是这个回调函数的第一个参数req,用户请求的信息在这个对象中,可以获取到用户的信息,比如ip,请求信息等。//第二个参数res是什么服务器返回给用户的信息console.log('jiengu')res.setHeader("Content-Type","text/html;charset=utf-8")//设置响应头的content-type内容,text/html是把responsebody当做HTML解析,res.write('
HungerValley
')//将服务器返回的内容写入res中的浏览器res.end()})server.listen(9000)//通过listen方法启动它,服务器监听9000端口2、执行步骤打开gitbash,切换到js文件所在文件夹,然后进入nodeindex.js(index.js)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.2response.writeHead()writeHead文档规范格式:response.writeHead(statusCode,statusMessage)参数1statusCode)为三位HTTP状态码,如404。参数2为statusMessage为一个可选的状态描述,它是一个字符串。参数3headers是响应头,是一个对象。其实我们可以理解为这个对象放了响应头的全部内容。我们设置的writehead的内容处理状态码放在general中,其他内容封装成一个对象放在responseheader内容responseheaders中。response.writeHead(404,'NotFound')res.writeHead(200,'hhh',{'Content-Type':'text/plain;charset=utf-8','X-Foo':'bar2222'});4.3两者的区别response.writeHead()在消息中只能调用一次,必须在response.end()调用之前调用。调用两次会报错。setheader可以多次调用标头。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只能写一次,必须设置所有响应头内容必须放在参数三根据对象的格式标头。以下缩写是正确的,记住res.writeHead(200,'hhh',{'Content-Type':'text/plain;charset=utf-8','X-Foo':'bar2222'});坑3:response.setHeader()设置的响应头会和response.writeHead()设置的响应头合并,但是如果设置的内容重复,则以response.writeHead()的优先级为准。varserver=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('
饥饿谷2
')res.end()})server.listen(9000)执行结果为:可以明显看出setHeader和writeHead的重复设置是基于writeHead的4.5设置状态的异常res.writeHead(404,'hhh');当我设置状态为404时,发现即使请求成功回传,也会出现红色。这是因为大家一致认为404是错误状态,所以status的值要按照约定来设置。2.一个可用的静态服务器用图片、css、js资源搭建服务器,github代码链接1.步骤我在step1文件夹下放了server.js文件和static文件夹。static文件夹对应css、png、js、html等文件,在html文件中引用图片、css、js资源。打开gitbash,切换step1文件夹,执行nodeserver.js,打开浏览器,输入localhost:8080index.html,查看结果输出内容2、js代码分析varhttp=require('http')varpath=require('path')//path模块处理url,不同系统(mac/lincx/window)下url的写法可能不一致。(一个写成c:/project/code/a.png//,另一个可能写成/user/local/project/a.png)。path模块会自动处理这种情况urltypevarfs=require('fs')//fs模块用于读取文件数据,也可以向文件写入数据。varurl=require('url')//url模块可以自动解析url,得到一个对象,得到相应的信息。functionstaticRoot(staticPath,req,res){console.log(staticPath)//输出静态文件的绝对路径,/user/documents/code/node-server/step1/staticconsole.log(req.url)//request第一次调用html时为/index.html,第二次调用css时为css/a.cssvarpathObj=url.parse(req.url,true)//parseurl并得到url对象(包括protocol/hostname/port/pathname/query等),即pathobj对象就是url的对象。这次我们会使用pathnameconsole.log(pathObj)if(pathObj.pathname==='/'){pathObj.pathname+='index.html'}//如果没有输入pathname(浏览器输入的值就是localhost:8080,如果没有后缀),服务器默认选择读取并发送index.html文件varfilePath=path.join(staticPath,pathObj.pathname)//staticPath=静态文件夹的绝对路径,pathObj.pathname=call文件的后缀地址。//两者相加得到filePath(用户访问文件输入的url的绝对路径),比如这篇文章是/user/documents/code/node-server/step1/static/index.html//var文件内容=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,'notfound')res.end('
404NotFound
')//页面显示404NotFoundres.end('data')中等于执行res.write('data')加上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地址。//服务端根据解析地址在本地静态文件夹中找到对应的index.html文件,读取html中的数据,将数据放入res中,以字符串的形式发送给服务端。varserver=http.createServer(function(req,res){staticRoot(path.join(__dirname,'static'),req,res)//写一个staticRoot函数来处理请求。/*参数1:whichpathAs一个静态文件路径,传递路径名。__dirname是nodejs中的一个变量,代表当前server.js执行的文件。path.join(__dirname,'static')可以使用一个或多个字符串值参数,Parameters返回通过组合这些字符串值参数形成的路径。varjoinPath=path.join(__dirname,'a','b','c');console.log(joinPath);//D:\nodePro\fileTest\a\b\c,__dirname对应的step1文件夹的路径,加上static文件夹的路径,等于static的绝对路径,这样做的好处是每次绝对路径变化时,不需要再次修改Absolutepath.*/})server.listen(8080)//创建服务器并监听8080端口console.log('visithttp://localhost:8080')3.代码难点分析3.1路径中node.js文档标准解释用于处理文件和目录路径的路径模块。不同系统(mac/lincx/window)下url的写法可能不一致。(一个写成c:/project/code/a.png//,另一个可能写成/user/local/project/a.png)。path模块会自动处理这种情况urltype3.2path.join([...paths])parameter...paths
:pathfragmentssequence,return:useplatform-specificdelimiterstojoinallpath片段连接在一起并规范化生成的路径path.join('C:\Users\jz\documents\code\node-server\step1','static')//C:\Users\jz\documents\code\node-server\step1\static3.3fs文件系统node.js文档中的标准解释fs模块用于以类似于标准POSIX函数的方式与文件系统进行交互。所有的文件系统操作都有同步和异步两种形式。异步表单的最后一个参数是完成时的回调函数。传递给回调函数的参数取决于具体的方法,但第一个参数是为异常保留的。如果操作成功完成,第一个参数将为null或未定义。3.4fs.readFile(path[,options],callback)异步读取文件路径文件名或文件路径options的内容如果options是字符串,指定字符编码,默认为nullcallback是一个回调函数,有两个参数(err,data),其中data是要读取的文件内容fs.readFile(filePath,'binary',function(err,fileContent){//异步方式读取filePath的文件。binary是指二进制方式读取数据,因为服务器不仅要读取普通数据,还需要兼容图片、文件等数据if(err){console.log('404')res.writeHead(404,'notfound')res.end('404NotFound
')//在页面上显示404NotFound。在res.end('data')等于执行res.write('data')加上res.end()}else{console.log('ok')res.writeHead(200,'OK')res.write(fileContent,'binary')//以二进制形式发送数据res.end()}})3.5fs.readFileSync(path[,options])同步读取文件内容,两个参数的用法与异步相同//varfileContent=fs.readFileSync(filePath,'binary')//res.write(fileContent,'binary')////同步读取filePath的文档,并将读取的数据写入res对象内//res.end()3.6url模块node.js文档中的标准解释url模块提供了一些用于URL处理和解析的实用函数。可以将URL字符串解析为URL对象,其属性对应于字符串的组件。3.7url.parse(urlString[,parseQueryString[,slashesDenoteHost]])url.parse()方法将解析一个URL字符串并返回一个URL对象。urlString要解析的URL字符串。parseQueryString如果为真,query属性将始终通过querystring模块的parse()方法生成一个对象。如果为false,则返回的URL对象的查询属性将是一个未解析、未解码的字符串。默认为假。slashesDenoteHost如果为true,则//之后和下一个/之前的字符串将被解析为主机。例如,//foo/bar将解析为{host:'foo',pathname:'/bar'}而不是{pathname:'//foo/bar'}。默认为假。例如varpathObj=url.parse(req.url,true)//解析req.url得到url对象pathobj3.8__dirname中当前模块的文件夹名。path.dirname()的值等于__filename__filename当前模块的文件名---解析后的绝对路径例如:executenodeexample.jsconsole.log(__filename);//打印在/Users/mjrdirectory:/Users/mjr/example.jsconsole.log(__dirname);//Prints:/Users/mjr4,坑里有问题,为什么我们用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挑出来。3.实现一个简单的node.js服务器路由来实现一个更复杂的服务器。url不仅仅是定位一个静态文件,还可以mock任何数据,和前端交互。1、核心原理:根据浏览器请求的不同路由,服务器会进行不同的操作。2、文件结构:3、服务端实现3条路由:/getWeather,结合b.js文件实现ajax模拟天气数据/user/123,结合user.tpl文件实现用户页面/index.html,结合index.html实现了index.html的页面。参考html、b.js和图4中的css文件,对应文件内容可以在github上查看代码。这里截图说明htmlcssjs,实现ajaxuser.tpl的代码这次最重要的server-simple.js服务端代码demourl是localhost:8080/user/123,localhost:8080后面的内容就是路由.所有对8080服务器的请求,根据不同的路由向浏览器发送不同的数据varhttp=require('http')varfs=require('fs')varurl=require('url')http.createServer(function(req,res){varpathObj=url.parse(req.url,true)console.log(pathObj)switch(pathObj.pathname){case'/getWeather'://根据req.url执行不同的函数varretif(pathObj.query.city=='北京'){ret={city:'北京',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;默认:res.end(fs.readFileSync(__dirname+'/static'+pathObj.pathname))}}).listen(8080)5.执行结果index.html/getWeather/用户/123