NodeJs简易web服务器 实现网页访问

874 阅读5分钟

前言

Web服务器一般指网站服务器,是指驻留于因特网上某种类型计算机的程序,可以处理浏览器等Web客户端的请求并返回相应响应,也可以放置网站文件,让全世界浏览;可以放置数据文件,让全世界下载。目前最主流的三个Web服务器是Apache、 Nginx 、IIS。

image.png

在以往,你写个网站,比如

  • JavaWeb,你要下载一个TomCat
  • ASP.NET,你要安装一个IIS

但是小伙子,我要告诉你,你走运了

学习Node.js后的你,啥都不用下载,啥都不用搭建,打开js文件撸一套代码就可以全部搞定。

(抛开静态网页和动态网页的类型不同不谈,我们这里只是做一个类似静态资源的服务器)(手动狗头)

正文

思路

首先,我们梳理一个平时访问网站的流程:

  1. 打开浏览器输入网站
  2. 服务器返回静态资源文件 例如html、css、js
  3. 浏览器自己解析这些文件,生成人人都看到的的网页

仔细分析下来

第一步我们可以用http模块实现

第二步可以结合fs模块进行文件读取,配合http模块返回给客户端

难点 我们的目标不仅仅只是简单的fs读取文件,http返回内容。

作为一个优雅的程序员,写这种代码实在有违优雅。

因此我的目标是这样的:

任意的文件夹只要丢到我的服务器目标目录下皆是可以被访问: 当我们访问一个html文件的时候,它关联的css、js文件都将自动被拼接所在的服务器路径并且被返回

代码实操

小试牛刀 搭建基础http服务

首先我们书写一段基本的http服务代码

并且尝试打印出request.url的路径看看,以此来分析下一步fs代码书写

// 导入相关模块
const http = require('http')
const fs = require('fs')
const path = require('path')

// 创建http服务
const server = http.createServer()

server.on('request', (request,response)=> {
  // 获取请求路径
    var reqURL = request.url;

   // 发送响应数据
  respond.writeHead(200,{'Content-Type':'text/html;charset=UTF8'});
  respond.end("您的请求路径为:"+reqURL); 
})

server.listen(8080, () => {
  console.log('http服务已经启动 在 http://127.0.0.1:8080')
})

运行一下看看:

image.png

image.png

image.png

经过多次尝试,发现该request.url会自动省略ip和端口号

并且

  • 根目录就是单纯的/
  • 而中文会被加密

修改一下代码,添加一句console.log尝试在后台打印一下看看服务器接收的效果

image.png

也是一样,中文被加密了

解决:URL中文编码解码

首先解答一下为什么在http传输时URL会被编码,例如中文变成一串%AD%A%SD····巴拉巴拉。 通常如果一样东西需要编码,说明这样东西并不适合传输。原因多种多样,如Size过大,包含隐私数据,对于Url来说,之所以要进行编码,是因为Url中有些字符会引起歧义。

在JavaScript中,大概有三种方法对url进行编码

但是在这里我只介绍其中之一,因为他们更常用:

  • 编码方法 encodeURI()
  • 解码方法 decodeURI()

使用方法也很简单,直接把目标url字符串作为参数传递进去即可

server.on('request', (request,response)=> {
  // 获取请求路径(升级版,解码中文)
  var reqURL = decodeURI(request.url);
  console.log(reqURL)
   // 发送响应数据
  response.writeHead(200,{'Content-Type':'text/html;charset=UTF8'});
  response.end(`<h3>您的访问路径为:${reqURL}</h3>`); 
})

image.png

image.png

由此解决了中文编码问题

拼接http请求文件所在的服务器绝对路径

梳理一下当前目标:

  • 从请求路径中 分析出目标文件所在服务器路径
  • 利用fs读取内容,response给客户端

首先拼接一下文件路径 我在此例举出path模块常用方法 以方便学习

  • path.join() 方法,用来将多个路径片段拼接成一个完整的路径字符串 。

  • path.basename() 方法,用来从路径字符串中,将文件名解析出来。

  • path.extname() 方法 使用extname方法获取路径中的文件的扩展名

  • __dirname指的是当前文件所在文件夹的绝对路径。

思路:

我们先用 __dirname 获取当前js脚本所在文件夹的绝对路径,细节:不写死绝对路径,而是用__dirname自动获取,是为了方便后期代码移动到其他服务器部署的可移植性

而后我们用path.join()方法去拼接路径,可以更加优雅规范的获得文件路径

// 导入相关模块
const http = require('http')
const fs = require('fs')
const path = require('path')

// 创建http服务
const server = http.createServer()

server.on('request', (request,response)=> {
  // 获取请求路径
  var reqURL = decodeURI(request.url);
  // 拼接url请求路径所在的服务器绝对路径
  var resourceURL = path.join(__dirname,'/theWorks',reqURL)
  //  // 发送响应数据
  // 根目录访问网页
  if (reqURL === '/') {
    response.writeHead(200,{'Content-Type':'text/html;charset=UTF8'});
    response.end(getResource(resourceURL)); 
  } else {
    // 非根目录拼接文件路径
    response.writeHead(200,{'Content-Type':'text/html;charset=UTF8'});
    response.end(getResource(resourceURL)); 
  }
})

server.listen(8080, () => {
  console.log('http服务已经启动 在 http://127.0.0.1:8080')
})

// 读取文件函数
function getResource (resourceURL) {
  return '为你读取 '+resourceURL
}

运行结果

image.png

按需读取文件并返回

之前我们简单定义了个文件函数,但并没有真正实现它的功能,这一步,我们来把它完善一下

// 导入相关模块
const http = require('http')
const fs = require('fs')
const path = require('path')

// 创建http服务
const server = http.createServer()

server.on('request', (request,response)=> {
  // 获取请求路径
  var reqURL = decodeURI(request.url);
  // 拼接url请求路径所在的服务器绝对路径
  var resourceURL = path.join(__dirname,'/theWorks',reqURL)
  var type = path.extname(resourceURL)
  //  // 发送响应数据
  // 根目录访问网页
  if (reqURL === '/') {
    // 默认访问html
    resourceURL = path.join(resourceURL,'index.html')
    response.writeHead(200,{'Content-Type':'text/html;charset=UTF8'});
    response.end(getResource(resourceURL,type)); 
  } 
  else if (type == 'webp') {
    // 是图片
    response.writeHead(200,{'Content-Type':'image/webp'});
    response.end(getResource(resourceURL,type)); 
  }
  else if (type == 'js') {
    // 是js文件
    response.writeHead(200,{'Content-Type':'text/javascript'});
    response.end(getResource(resourceURL,type)); 
  }
  else if (type == 'js') {
    // 是css文件
    response.writeHead(200,{'Content-Type':'text/css'});
    response.end(getResource(resourceURL,type)); 
  }
})

server.listen(8080, () => {
  console.log('http服务已经启动 在 http://127.0.0.1:8080')
})

// 读取文件函数
function getResource (resourceURL,type) {
  fs.readFile(resourceURL,(err,data) => {
    // 读取失败
    if (err) {
      console.log(err.toString())
      return '<h1>404 not found</h1>'
    }
    // 读取成功
    console.log(resourceURL)
    if (type=''){
      return data.toString()
    } else {
      return data
    }
  })
}

image.png

image.png

至于为什么没能触发多米诺骨牌那种效果:浏览器根据index.html不断发起多个文件请求 我也很懵逼

还有非文本的文件,例如ico、webp·····种种又该如何配置返回比较方便

此等项目过于玄妙高深,非我等初级js后端所能解决

待我好好修行几日,再来解决它