前言
Web服务器一般指网站服务器,是指驻留于因特网上某种类型计算机的程序,可以处理浏览器等Web客户端的请求并返回相应响应,也可以放置网站文件,让全世界浏览;可以放置数据文件,让全世界下载。目前最主流的三个Web服务器是Apache、 Nginx 、IIS。
在以往,你写个网站,比如
- 写
JavaWeb,你要下载一个TomCat - 写
ASP.NET,你要安装一个IIS
但是小伙子,我要告诉你,你走运了
学习Node.js后的你,啥都不用下载,啥都不用搭建,打开js文件撸一套代码就可以全部搞定。
(抛开静态网页和动态网页的类型不同不谈,我们这里只是做一个类似静态资源的服务器)(手动狗头)
正文
思路
首先,我们梳理一个平时访问网站的流程:
- 打开浏览器输入网站
- 服务器返回静态资源文件 例如html、css、js
- 浏览器自己解析这些文件,生成人人都看到的的网页
仔细分析下来
第一步我们可以用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')
})
运行一下看看:
经过多次尝试,发现该request.url会自动省略ip和端口号
并且
- 根目录就是单纯的
/ - 而中文会被加密
修改一下代码,添加一句console.log尝试在后台打印一下看看服务器接收的效果
也是一样,中文被加密了
解决: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>`);
})
由此解决了中文编码问题
拼接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
}
运行结果
按需读取文件并返回
之前我们简单定义了个文件函数,但并没有真正实现它的功能,这一步,我们来把它完善一下
// 导入相关模块
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
}
})
}
至于为什么没能触发多米诺骨牌那种效果:浏览器根据index.html不断发起多个文件请求 我也很懵逼
还有非文本的文件,例如ico、webp·····种种又该如何配置返回比较方便
此等项目过于玄妙高深,非我等初级js后端所能解决
待我好好修行几日,再来解决它