这是我参与更文挑战的第3天,活动详情查看: 更文挑战
前言
今天,我们主要说nodejs中的Http
模块,这个模块可以说是每个前端开发接触nodejs
的第一个模块了,那我们接下来具体讲讲,Http模块主要做了什么,废话不多说,我们直接上代码:
const http = require('http');
http.createServer((request, response) => {
response.end('Hello Nodejs');
}).listen(3000, () => {
console.log('server start up at 3000')
})
如上所写,这样我们就创建了一个最简单的nodejs服务,是不是很简单,这大概就是他受欢迎的原因吧。但是作为一个test服务,这样完全可以了,我们可以借助这种方式做到一些事情,但是当我们想做的事情变多(可能不用与生产),但这样一个服务,很明显是无法支撑我们进行我们前端页面的调试,那一个最基本可用的服务需要包括那些部分呢?具体如下
简单可用服务需要包括那些东西
- 需要可以处理
get
,post
请求 - 对
post
参数的处理 - 需要可以处理静态文件
- 需要可以处理跨域请求
今天,我们暂时先说以上四点吧,当然涉及到的地方肯定还有很多,比如
FormData
参数的处理,文件参数的处理,中间件的处理等等,如果对这些感兴趣的话,建议大家先简单看一下express
或者koa
的api设计,在不查看源代码的情况下,对某一方向处理进行开发,然后对比源码,大概是对nodejs学习最有效的方式了,废话不多说,我们一个一个点看:
处理get post请求
处理get post请求,其实很简单,if else 一把梭就可以,具体如下:
const http = require('http');
http.createServer((request, response) => {
const {url, method} = request;
if(url === '/' && method.toLowerCase() === 'get') {
response.setHeader('Content-Type', 'text/plain;charset=utf-8')
response.end("你访问的是首页!");
} else {
response.statusCode = 404;
response.end();
}
}).listen(3000, () => {
console.log('server start up at 3000')
})
像这种方式,再处理一些简单逻辑时当然是可以的,但是当我们涉及到的业务越来越多,势必要对接口进行规范化处理,而不是像现在这样,那我们是否可以这样做呢?编写一个js,用来获取某个固定文件夹下的js文件进行接口的处理,具体如下:
// loader.js 负责读取routes的文件
const { readdir } = require("fs/promises")
module.exports = {
load: (path, callback) => {
readdir(path)
.then(files => {
files.forEach(filename => {
callback(filename);
})
})
}
}
// 编写route文件
// index
module.exports = {
'get /': (request, response) => {
response.setHeader('Content-Type', 'text/plain;charset=utf-8');
response.end('这是首页的内容');
},
'get /detail': (request, response) => {
response.setHeader('Content-Type', 'text/plain;charset=utf-8');
response.end('这是首页详情页')
}
}
// home
module.exports = {
'get /': (request, response) => {
response.setHeader('Content-Type', 'text/plain;charset=utf-8');
response.end('这是home的内容');
},
'get /detail': (request, response) => {
response.setHeader('Content-Type', 'text/plain;charset=utf-8');
response.end('这是home详情页')
}
}
// httpService对文件进行加载并执行函数
const http = require('http');
const { url } = require('inspector');
const { resolve } = require('path');
const { load } = require('./loader');
http.createServer((request, response) => {
let {url, method} = request;
method = method.toLowerCase();
load(resolve(__dirname, 'routes'), filename => {
const prefix = filename === 'index.js' ? '' : '/' + filename.split('.')[0];
const module = require(`./routes/${filename}`);
Object.keys(module).forEach(str => {
let [currMethod, currUrl] = str.split(' ');
currUrl = prefix + currUrl;
if(url === currUrl && currMethod === method) {
module[str](request, response)
}
})
})
}).listen(3000, () => {
console.log('server start up at 3000')
})
如上所述,我们解决了http服务的if else嵌套问题,在每次访问时进行文件的读写操作,根据读出的配置进行函数的调用,那这个时候我们可以发现,我们无法拿到post的body数据,那我们应该如何做呢?请继续往下看:
post参数基本处理
众所周知,http的createServer提供的request参数和response都是基于流的,request类型为post时,参数会通过流的方式进行传递,那我们就需要监听某些方法,从而让我们可以拿到HttpRequest的完整参数,具体代码如下:
const http = require('http');
const { resolve } = require('path');
const { load } = require('./loader');
http.createServer((request, response) => {
let {url, method} = request;
method = method.toLowerCase();
// post body的处理
const data = [];
request.on('data', (result) => data.push(result));
request.on('end', () => {
request.data = Buffer.concat(data).toString()
// 读取文件进行url和method的比对
load(resolve(__dirname, 'routes'), filename => {
const prefix = filename === 'index.js' ? '' : '/' + filename.split('.')[0];
const module = require(`./routes/${filename}`);
Object.keys(module).forEach(str => {
let [currMethod, currUrl] = str.split(' ');
currUrl = prefix + currUrl;
if(url === currUrl && currMethod === method) {
module[str](request, response)
}
})
})
})
}).listen(3000, () => {
console.log('server start up at 3000')
})
这个时候,我们的请求实际上已经可用了,但是因为浏览器跨域限制的原因,ajax post方法并不能正确返回参数,那具体应该怎么做呢?我们接着往下看
解决跨域问题
解决跨域问题,现在最常用的有以下两种(当然还有其他,有时间的话会总结),CROS设置和设置代理,具体设置代理的方式我们可以在下次分享时着重讲一下,今天先说CROS,CROS是几个常用的http请求头的设置,包括一下几个:
- Access-Control-Allow-Origin 此项添加的是当前请求允许访问的域名
- Access-Control-Allow-Headers 此项添加的是允许发送的请求头信息(涉及复杂请求)
- Access-Control-Allow-Credentials 允许携带cooke 具体代码如下:
module.exports = {
'post /': (request, response) => {
// 正常环境不应该设置*,为了测试设置*
response.setHeader('Content-Type', 'text/plain;charset=utf-8');
response.setHeader('Access-Control-Allow-Origin', '*');
response.setHeader('Access-Control-Allow-Headers', '*');
response.setHeader('Access-Control-Allow-Credentials', true);
response.end('这是home的内容' + request.data);
},
'get /detail': (request, response) => {
response.setHeader('Content-Type', 'text/plain;charset=utf-8');
response.end('这是home详情页')
}
}
解决静态文件代理问题
关于静态文件的代理问题,首先我们要有一个静态文件目录,在每次请求进入时,根据请求路径和静态文件目录一起进行文件的检索,如果存在,则返回文件,不存在则返回404,值得注意的是,正常文件处理中包括html/css/js/image/font/等文件,我们可能没法覆盖到全部文件,大家如果感兴趣,可以查看express.static的源码看看框架层面如何处理,今天我们的例子就拿最简单的进行处理:
const filename = resolve(__dirname, temp, url.slice(1));
stat(filename).then(
result => {
if(result.isFile()) {
readFile(filename)
.then(result => {
response.end(result);
})
}
},
err => {
response.statusCode = 404;
response.end();
}
)
结语
以上就是今天的全部内容,大家有想要看的模块可以在评论区提出,具体代码地址,大家感兴趣的也可以下载express或者koa的代码对他们的结构功能进行分析,一定会有不小的收获!