webserver业务逻辑梳理
- 创建一个webserver.js文件
- 在与webserver.js文件同级创建一个public文件夹,在public文件夹下的static文件夹下创建js、css、images文件夹
– 在public创建一个index.html文件 - 在与webserver.js文件同级创建config.js文件
– 在config文件里创建三个变量wwwroot、indexArr、errorOut,
– 找到网站根目录并赋值给wwwroot,变量indexArr,用来存放入口文件名(不加/的文件名),errorOut变量用来存放一个对象{403:,404:,500:}每个键对应的值就是对应的错误页面相对于电脑根目录的相对地址.
– 将对象{wwwroot,indexArr,errorOut}暴露出去
– webserver.js文件里面引入config.js文件
– 创建一个router文件
创建一个对象router{自定义的路径名:js文件路径@js文件里的方法名}
暴露出router对象 - 在webserver.js同级创建一个controller文件夹,里面存放一个test.js
– 在test.js写两个函数,并把函数暴露出来 - 在与webserver.js文件同级创建一个errorpages文件夹存放错误页面
– 在errorpages文件夹创建一个404.html文件
– 在errorpages文件夹创建一个403.html文件
– 在errorpages文件夹创建一个500.html文件
webserver.js文件内容:
- 引入http模块,将其结果赋值给一个变量
- 引入fs模块,将其结果赋值给一个变量
- 引入querystring模块,将其结果赋值给一个变量
- 引入mime模块,将其结果赋值给一个变量(要记得npm install --save mime模块)
- 引入config文件,将声明一个对象,对象里面的几个变量(解构)对应config暴露出去的名字.
- 引入router文件并赋值给变量Router
- 声明两个全局变量,request、response并赋值为null(为了不用传参,并且这两个变量极其特殊)
- 创建一个httpserver对象
- 监听httpserver的request事件
– 当有请求时,把实参请求对象赋值给全局变量request
– 为request创建一个params键并赋值一个对象{get:"",post:""}
– 通过“?”切割请求地址,得到的后半部分赋值给request的params的get,并将其通过querystring解析成一个对象
– 创建监听请求对象的输入流(data)事件,这个事件的句柄里面的形参为chuck,用request的params的post接收一个个chuck,并且拼接起来
– 创建监听请求对象的end,这个事件句柄如下:
把request的params的post的内容通过querystring解析成一个对象
声明一个变量url接收req.url里面的地址部分(通过“?”切割请求的地址的0下标)
调用找函数,将变量url作为实参传入找函数,用一个变量pathObj接收找函数的返回值,
调用读函数,向读函数传入的参数是pathObj里面的完整路径、函数名,声明变量dataObj接收读函数的返回值
调用返函数,向返函数里面传入pathObj里面的状态码、dataObj里的读取的数据和文件类型 - httpserver监听一个端口
封装找函数
- 找函数的意义是查找文件地址以及相应的状态码
- 找函数的参数是地址
- 找函数的返回值是一个对象,对象{完整路径,状态码}
声明一个对象用来存放{路径和状态码}
首先判断路由里面是否有该请求的地址的键,如果有就获取该键的值,切割该值得到一个文件地址赋值前面声明的对象,并且给对象创建一个方法名的键存放切割后得到的一个函数名,并且状态 码为200,并作为函数的返回值
声明一个真实路径,把wwwroot+形参赋值给真实路径
判断真实路径是否存在,如果存在就返回对象{真实路径和状态码200},不存在就返回{404对应的路径和状态码404}
判断路径是否为文件夹或者文件,如果不是文件,就调用找入口文件函数,路径作为参数,返回 找入口文件的返回值
判断该文件是否有读的权限,如果没有就返回对象{403对应的路径和状态码403}
如果以上if都通过,则返回一个正常的{拼接后路径和状态码200}
封装读函数
- 函数的意义是读取文件类型,解析文件内容
- 读函数的参数是路径、函数名
- 读函数的返回值是对象{读取的数据,文件类型}
声明一个对象{data,文件类型}
根据传入的地址,用split切割.生成一个新数组
将新数组的后半部分赋值给一个变量,此变量即为后缀名
进行判断(后缀名为js且request的params的get的api存在且存在函数名)
- 足条件则解析js文件,并执行对应的函数,函数传一个request,将函数返回结果赋值给对象里面的data
- 清除解析js文件的缓存
- 文件类型解析为json,并赋值给对象里面的文件类型
- 返回对象{data,文件类型}
- 使用mime模块判断变量是什么文件类型
- 读取传入的地址所对应的文件内容
- 返回对象{data,文件类型}
封装返函数
- 返函数的参数是状态码、读取的数据、文件类型
- 返函数的意义:将数据发送给客户端
– 调用response下的writeHead函数,把状态码、文件类型传进去
– 调用response下的write函数,把data传进去
– 调用response下的end函数
入口文件函数
- 判断传入的地址的最后一位是否为"/",如果不为"/",拼接上"/".
- 遍历入口文件(indexArr)数组,数组元素依次拼接传过来的路径的,判断文件是否存在,只要有一个存在就返回{拼接后的路径和状态码200},如果不存在{403对应的路径和状态码403}
//引入模块
const http = require("http");
const fs = require("fs");
const mime = require("mime");
const querystring = require('querystring');
const router = require('./router.js');
//引入配置文件
const { root, errorpages, indexArr } = require("./config.js");//声明的变量名和对象的键名一致
let server = http.createServer()
let request = null;
let response = null;
server.on("request", function (req, res) {
request = req;
response = res;
request.params = { get: "", post: "" };
//post参数的获取:
req.on("data", function (chunk) {
request.params.post += chunk;
})
req.on("end", function () {
// 解析参数
request.params.get = querystring.parse(req.url.split("?")[1]);
request.params.post = querystring.parse(request.params.post);
//找,返回查找文件和状态码
let pathObj = findPath(req.url.split("?")[0]);
// console.log(pathObj)
//读
let contentObj = readFile(pathObj);
// console.log(contentObj)
//返
writeFile(contentObj, pathObj.code);
})
})
//找函数
function findPath(url) {
let pathObj = { path: "", code: 200 };
// 找路由
if (router[url]) {
pathObj.path = router[url].split("@")[0];
pathObj.funName = router[url].split("@")[1];
pathObj.code = 200;
return pathObj;
}
root[root.length - 1] == "/"
? pathObj.path = root.slice(0, root.length - 1) + url
: pathObj.path = root + url;
// fs.exists(path,callback)来检查目录文件是否存在,fs.existsSync:以同步的方法检测目录是否存在。如果目录存在返回 true ,如果目录不存在 返回false
if (!fs.existsSync(pathObj.path)) {
return { path: errorpages[404], code: 404 }
}
//判断该文件类型是否是一个文件,如果不是文件,则在这个目录下面查找
if (!fs.statSync(pathObj.path).isFile()) {
return exportfile(pathObj.path);
}
//判断是否有读取文件的权限, //如果没有返回403;
if (!isaccess(pathObj.path)) {
return { path: errorpages[403], code: 403 }
}
return pathObj;
}
//多个入口文件
function exportfile(path) {
path = path[path.length - 1] == "/" ? path : path + "/"
for (let i = 0; i < indexArr.length; i++) {
let pathObj = { path: path + indexArr[i], code: 200 }
if (fs.existsSync(pathObj.path)) {//如果目录不存在
return pathObj;
}
}
return { path: errorpages[403], code: 403 }
}
//判断是否有读取文件的权限
function isaccess(url) {
try {
fs.accessSync(url, fs.constants.R_OK);
return true;
} catch (err) {
return false;
}
}
//读文件要使用了mine模块同步方式读取文件
function readFile(pathObj) {
let resObj = { data: "", fileType: "" };
let urlTempArr = pathObj.path.split(".");
let type = urlTempArr[urlTempArr.length - 1];
//api请求
if (type == "js" && request.params.get.api && pathObj.funName) {
resObj.data = require(pathObj.path)[pathObj.funName](request);
delete require.cache[pathObj.path];
resObj.fileType = mime.getType("JSON")
return resObj;
}
resObj.fileType = mime.getType(type);
resObj.data = fs.readFileSync(pathObj.path);//同步
return resObj;
}
//返函数,content文件内容,res:响应对象,writeHead 的返回值是res本身
// let pathObj = { path: "", code: ""};
// let contentObj = readFile(pathObj, funName)
// writeFile(contentObj, resObj, pathObj.code);
function writeFile(contentObj, code) {
response.writeHead(code, {
'Content-Type': contentObj.fileType,
'Access-Control-Allow-Origin': '*', //支持全域名访问,不安全,部署后需要固定限制为客户端网址
'Access-Control-Allow-Methods': 'POST,GET,OPTIONS,DELETE',//支持的http 动作
'Access-Control-Allow-Headers': 'x-requested-with,content-type' //响应头 请按照自己需求添加。
})
response.write(contentObj.data);//文件内容
response.end();
}
// server.listen(8080, function () {
// console.log('服务器运行在http://192.168.1.22:8080/')
// });
module.exports = server;