前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >使用noode.js创建一个服务器

使用noode.js创建一个服务器

作者头像
bamboo
发布2019-01-29 15:51:23
1.4K0
发布2019-01-29 15:51:23
举报

一、简单的静态服务器

1、代码解析

代码语言:javascript
复制
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文件名,反正你们取什么名就输入啥名)

clipboard.png
clipboard.png

打开浏览器,输入http://127.0.0.1:9000/,或者http://localhost:9000/ 注意哈9000是代码里面写的9000端口,如果下次改成了8080等其他的端口,那就改成对应的端口就好

clipboard.png
clipboard.png

3、响应头和响应体

响应头查看路径:network-name-headers

clipboard.png
clipboard.png

响应体: 响应体是response的数据,有点类似于打开网页的查看源代码

clipboard.png
clipboard.png

每次修改了js文件的内容之后,要断掉git的服务器,重新连接。不然即使刷新网页没有办法显示修改的内容

4、设置响应头

4.1response.setHeader 格式:response.setHeader(name, value) 为一个隐式的响应头设置值。 如果该响应头已存在,则值会被覆盖。 如果要发送多个名称相同的响应头,则使用字符串数组。 非字符串的值会保留原样,所以 response.getHeader() 会返回非字符串的值。 非字符串的值在网络传输时会转换为字符串。 举例:

代码语言:javascript
复制
response.setHeader('Content-Type', 'text/plain'); //当成字符串解析
代码语言:javascript
复制
response.setHeader('Content-Type','text/html; charset=utf-8')//当成html解析,如果是css就设置为text/css

执行结果

clipboard.png
clipboard.png

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。

clipboard.png
clipboard.png
代码语言:javascript
复制
 response.writeHead(404, 'Not Found')
代码语言:javascript
复制
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的一部分)

clipboard.png
clipboard.png

如果分开写成下面的格式,不会报错,但charset就变成了响应头的单独子项展示,而且charset=utf-8不会生效(下图utf-8没有生效就按照gbk去解码,就出现了乱码)。

代码语言:javascript
复制
res.setHeader('Content-Type', 'text/html');
res.setHeader("charset","utf-8")
clipboard.png
clipboard.png

所以一定注意写法

坑2:writeHead只能写一次,所有响应头要设置的内容都要按照对象的格式,放在参数三headers里面。以下缩写是正确的,要记住啊

代码语言:javascript
复制
 res.writeHead(200,'hhh', { 'Content-Type':'text/plain;charset=utf-8','X-Foo':'bar2222'});

坑3:response.setHeader() 设置的响应头会与 response.writeHead() 设置的响应头合并,但是如果设置的内容重复,以response.writeHead() 的优先为准。

代码语言:javascript
复制
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为准的

clipboard.png
clipboard.png

4.5设置status的异常

代码语言:javascript
复制
 res.writeHead(404,'hhh');

当我设置status为404,发现即使是请求成功回送之后,也会出现红色。这是因为大家约定404就是一个错误的状态,所以status的值要按照约定来设置

clipboard.png
clipboard.png

二、一个可用的静态服务器

搭建一个有图片,css,js的资源的服务器,github代码链接

1、步骤

  1. 我在step1文件夹下放置了server.js文件,static文件夹。static文件夹对应放了css,png,js,html等文档,并在html文档内引用了图片,css,js资源。
  2. 打开gitbash,切换step1文件夹,执行node server.js
clipboard.png
clipboard.png
  1. 打开浏览器输入localhost:8080index.html,查看结果
clipboard.png
clipboard.png

输出内容

clipboard.png
clipboard.png
clipboard.png
clipboard.png

2、js代码解析

代码语言:javascript
复制
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 片段连接到一起,并规范化生成的路径

代码语言:javascript
复制
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 是要读取文件的内容

代码语言:javascript
复制
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]) 同步的读取文件内容,两个参数和异步的一样的用法

代码语言:javascript
复制
// var fileContent = fs.readFileSync(filePath,'binary')
  // res.write(fileContent, 'binary')
  // // 采用同步的方式读取filePath的文档,把读取的数据写入res对象内
  // res.end()

3.6 url模块node.js文档中的标准解释 url 模块提供了一些实用函数,用于 URL 处理与解析。 URL 字符串可以被解析为一个 URL 对象,其属性对应于字符串的各组成部分。

clipboard.png
clipboard.png

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。 举个例子

代码语言:javascript
复制
var pathObj = url.parse(req.url, true)// 解析req.url,得到url对象pathobj
clipboard.png
clipboard.png

3.8__dirname 当前模块的文件夹名称。等同于 __filename 的 path.dirname() 的值 __filename 当前模块的文件名称---解析后的绝对路径 例如: 在 /Users/mjr 目录下执行 node example.js

代码语言:javascript
复制
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、文档结构:

clipboard.png
clipboard.png

3、服务器实现3条路由:

  • /getWeather,结合b.js文件实现一个ajax来mock天气数据
  • /user/123 ,结合user.tpl文件实现用户页面
  • /index.html,结合index.html实现index.html的页面。在html引用css文件,b.js,和图片

4、对应的文件内容

可以查看GitHub上面的代码,我这里截图说明

html

clipboard.png
clipboard.png

css

clipboard.png
clipboard.png

js,实现ajax的代码

clipboard.png
clipboard.png

user.tpl

clipboard.png
clipboard.png

最重要的server-simple.js服务器代码 本次演示的url是localhost:8080/user/123,localhost:8080之后的内容是路由。所有请求到8080这个服务器内,根据不同的路由给浏览器发送不同的数据

代码语言:javascript
复制
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

clipboard.png
clipboard.png

/getWeather

clipboard.png
clipboard.png

/user/123

clipboard.png
clipboard.png
本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
原始发表:2018-12-02,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 一、简单的静态服务器
    • 1、代码解析
      • 2、执行步骤
        • 3、响应头和响应体
          • 4、设置响应头
          • 二、一个可用的静态服务器
            • 1、步骤
              • 2、js代码解析
                • 3、代码难点解析
                  • 4、坑
                  • 三、实现一个简单的node.js服务器路由
                    • 1、核心原理:
                      • 2、文档结构:
                        • 3、服务器实现3条路由:
                          • 4、对应的文件内容
                            • 5、执行结果
                            领券
                            问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档