本文介绍: 本文是我开始系统学习Node.js的一些笔记,望对大家有帮助!如果文中阐述不全或不对的,请博友们在评论区指正,提前谢谢!!

【版权声明】未经博主同意,谢绝转载!(请尊重原创,博主保留追究权)
https://blog.csdn.net/m0_69908381/article/details/134544523
出自【进步*于辰的博客

接触Node.js挺长时间了,工作也经常使用,只是接口开发更倾向于业务的梳理,对基础要求反而不高。就是,参考前辈的代码路由,工作重心在接口代码
基础不牢固,不仅影响工作效率,而且不利于个人发展,所以近期开始恶补JS服务端技术的基础,就从Node.js开始。

1、关于 Buffer

详述可查阅博文【02-Node.js—Buffer(缓冲器)】(转发)。
参考笔记三,P49.1。

Buffer是一种类似数组对象用于表示固定长度的字节序列,其本质是一段内存空间,且空间由

c

+

+

color{green}{c++}

c++申请,每个元素占一个字节

创建

color{brown}{创建:}

创建

  1. Buffer.alloc(size)创建长度为 size 的字节序列
  2. buffer.allocUnsafe(size):同上,区别是在分配内存时不会清除旧数据(指曾使用过仍保留数据、但目前未使用内存空间);
  3. Buffer.from(xx)xx 可以数组字符串或 Buffer。

说明:

color{red}{说明:}

说明:
1、由于每个元素占一个字节,故alloc(size)allocUnsafe(size)创建的字节序列共包含 size 个字节。
示例

var buf = Buffer.alloc(10)
// 打印buf:<Buffer 00 00 00 00 00 00 00 00 00 00&gt;

规定以16进制的格式进行显示,00(16进制)是0000 0000二进制),共10个元素。

2from(xx)创建的字节序列所占字节数由 xx 决定。
示例1。(xx数组

var arr = [2, 0, 2, 3]
var buf = Buffer.from(arr)
// 打印buf:<Buffer 02 00 02 03&gt;

数字占一个字节,故长度为4
2(数字,十进制)是02(16进制)。

示例2.。(xx字符串

var buf = Buffer.from('2023')
// 打印buf:<Buffer 32 30 32 33&gt;

为何buf[0]32?因为此时的2不是数字,而是字符
'2'

A

S

C

L

L

color{blue}{ASCLL码}

ASCLL50转换成16进制就是32

示例3。(xx字符串

var buf = Buffer.from('汉字')
// 打印buf:<Buffer e6 b1 89 e5 ad 97&gt;

是不是有点懵?因为Buffer采用utf-8编码,一个汉字占3个字节,故用三个元素表示一个汉字。
改一下。

var buf = Buffer.from('汉字')
buf[0] = 97 + 7// 'h'的ASCLL码
buf[1] = 97
buf[2] = 97 + 13
console.log(buf.toString())// 打印:han字

toString()会将每个元素都转换成对应的字符,这样是不是一目了然了。
再补充一点。

var buf = Buffer.from('汉字')
buf[0] = 97 + 7 + 256// ------------------A
buf[1] = 97
buf[2] = 97 + 13
console.log(buf.toString())// 打印:han字

97 + 7'h'

A

S

C

L

L

ASCLL码

ASCLL,再+ 256已经不是'h',为何最后还是'h'
因为Buffer规定,一个字符占一个字节。换言之,只会用一个字节来表示字符,如果字符对应的

A

S

C

L

L

ASCLL码

ASCLL超出一个字节(8位)的表示范围255),超出的部分会被丢弃。
256对应的二进制1 00000000,即需要两个字节,则第一个字节舍去,剩下0000 0000,为0(十进制)。

示例4。(xx 是Buffer

var buf1 = Buffer.from([2, 0, 2, 3])
var buf2 = Buffer.from(buf1)
// 打印buf:<Buffer 02 00 02 03&gt;

与示例1相同。

2、fs模块

详述可查阅博文【03-Node.js—fs模块】(转发)。
参考笔记三,P50、P51。

fs模块是Node.js的内置模块,负责与文件系统的交互。

2.1 读文件

函数

  1. 异步读取:readFile(path[, options], (err, content) =&gt; {})
  2. 同步读取:var content = readFileSync(path[, options])
  3. 流式读取:(1)创建读取流:createReadStream(path[, options]);(2)通过'data''end'事件读取。

注:options是读取配置,如:'utf-8',否则读取结果二进制序列

2.2 写文件

函数

  1. 异步写入writeFile(path, data[, options], err => {})
  2. 同步写入writeFileSync(path, data[, options])返回undefinied
  3. 流式写入:(1)创建写入流:createWriteStream(path[, options]);(2)写入:write(data)
  4. 附加写入:appendFile() / appendFileSync()参数同上。

注:options是写入配置,如:{flag: 'a'}表示附加写入(暂不理解)。

3、path模块

详述可查阅博文【04-Node.js—path模块】(转发)。
参考笔记三,P51。

函数

  1. 解析路径resolve(path),path 前常附加当前目录__dirname
  2. 返回文件后缀:extname(path)

4、express模块

详述可查阅博文【09-Node.js—express框架】(转发)。
参考笔记三,P46、P50、P51。

express模块是基于Node.js的web应用开发框架,主要用于搭建js服务器

4.1 响应相关函数

  1. 重定向res.redirect(url)
  2. 响应文件:res.download(path),path 可以是绝对 / 相对路径,此函数是基于fs.readFile()res.end()封装
  3. json字符串作为响应体:res.json({})
  4. 设置响应体:res.send(),此函数是基于res.end()http模块)的封装,可响应任何类型,且只保留数据部分,如会将str最外层的''/""省略。

    注意:

    color{red}{注意:}

    注意:函数设置响应体,也是响应。虽是响应,但请求处理未结束,也由于是响应,故在其后不能再做响应配置,如:res.write()http模块)、res.set()(见第7项);

  5. 响应文件:res.sendFile(path.resolve(__dirname + path))。path 必须是绝对路径,故拼接了__dirname

4.2 中间件

1

路由中间件

color{green}{1、路由中间件}

1路由中间件
路由中间件”表现为具有三个参数req, res, next)的函数用于封装路由公共代码匹配路由前的操作),故需要置于所有路由之前(即中间件之前的路由不会执行中间件,因为路由匹配至上而下),且必须调用next()才能执行路由(暂不知next是什么)。

2

静态资源中间件

color{blue}{2、静态资源中间件}

2静态资源中间件
设置项目目录目录express.static(目录)

注意:

color{red}{注意:}

注意:中间件必须使用ser.use()引入。

4.3 Router

Router是一个完整的中间件和路由系统,可看作是一个小型的js服务器(当然并不是js服务器,故需要引入到js服务器使用)。

使用步骤

  1. 创建路由:var rou = express.Router()
  2. 配置路由(与js服务器相同);
  3. 开放接口module.exports = rou
  4. 引入:ser.use(require(Router路径)),路径必须是相对路径,且必须采用./格式

注意:

  1. 若使用 Router,必须在 Router 的路由的适当位置调用next()(先在路由回调函数添加next参数)。因为 Router 是子服务,一般使用 Router 时,主服务中肯定也有路由,如果不调用next(),则不会检索主路由;
  2. 留言

    color{red}{留言:}

    留言Router 没有跨域问题,原因未知。

4.4 解析请求数据

debug 一下,就可以发现req对象具有三个属性query(封装请求数据)、params(封装动态请求数据,暂不清楚)、body(封装请求体数据)。
假设body = {id: 2023},则通过req.body.idreq.body['id']即可获取参数id的值,之所以能获取到,是因为body中数据的格式json。换言之,若请求数据的格式不是json,就不一定能解析成功。
这时,可以使用body-parser模块进行辅助解析。

步骤

  1. (1)若请求数据封装的方式是x-www-form-urlencoded,构造对象var urlParser = bodyParser.urlencoded({extended: false});(2)若封装方式是json,构造对象var jsonParser = bodyParser.json()
  2. 将路由的第2个参数设置urlParser

留言

color{green}{留言:}

留言尝试了很多方法进行模拟测试ajaxpostman、form等等,可不知为何,req对象中始终没有body属性(js服务端使用vscode开发),故根本无法测试,所以只能请大家自行测试领悟了。

4.5 综合示例

先言:

color{red}{先言:}

先言:代码稍微有点长,一是为了尽量多使用express模块的函数,做个示例;二是为了使功能稍微丰满一点。大家阅读的时候,直接跳过与业务相关的代码,留意以上四个知识点相关的部分就OK。

1、主服务代码

const express = require('express')
const bodyParser = require('body-parser')
const path = require('path')

const ser = express()// 构建服务
// 引入静态资源中间件,
ser.use(express.static('./'))// 设置项目目录为当前目录

// 引入路由中间件
ser.use((req, res, next) => {
    res.set('access-control-allow-origin', 'http://127.0.0.1:5501')// 跨域配置
    next()
})

// 引入Router
ser.use(require('./r1'))// Router与当前文件同目录,文件名是 r1.js

var users = [
    {
        id: 1,
        name: '进步',
        pass: '2023'
    }, {
        id: 2,
        name: '于辰',
        pass: '2021'
    }
]

ser.get('/g1', (req, res) => {
    var id = req.query.userid
    var result = users.find(item => {
        if (item.id == id) {
            res.json({
                id: id,
                user: item.name,
                pass: item.pass
            })
            return true
        }
    })
    if(!result)// 未找到
        res.send('<h1>此账号异常</h1>')
})

var jsonParser = bodyParser.json()
ser.post('/p1', jsonParser, (req, res) => {
    var user = req.body.username
    var pass = req.body.password
    var result = users.find(item => {
        if(item.name == user &amp;&amp; item.pass == pass) {
            // 账号密码正确返回用户界面
            res.sendFile(path.resolve(__dirname + '/userinfo.html'))
        }
    })
    if(!result) {
        // 账号密码错误返回首页
        res.download('index.html')
    }
})

ser.all('*', (req, res) => {
    res.send('<h1>not found route</h1>')
})

// 启动服务,监听8081端口
ser.listen(8081, () => {
    console.log('created')
})

2、Router代码

const express = require('express')
const bodyParser = require('body-parser')

let rou = express.Router()// 创建Router

// Router是完整的中间件和路由系统,故也可在此创建路由中间件
// ser.use((req, res, next) => {
//     res.set('access-control-allow-origin', 'http://127.0.0.1:5501')
//     next()
// })

rou.get('/rou/g1', (req, res) => {
    var id = req.query.userid
})

var urlParser = bodyParser.urlencoded({extended: false})
rou.post('/rou/p1', urlParser, (req, res) => {
    var user = req.body.username
    var pass = req.body.password
})

rou.all('rou/*', (req, res, next) => {
    // 路由检索自上而下,若匹配此路由,说明此Router中没有”有效“匹配的路由,
    // 则调用 next() ”跳出“此Router,去主服务检索路由
    next()
})

module.exports = rou// 开放接口

5、http模块

详述可查阅博文【05-Node.js—http模块】(转发)。
参考笔记三,P50。

http模块是一个Node.js中与HTTP协议对接的模块,用于搭建HTTP服务,或者说用于搭建js服务器。

相关操作

color{red}{相关操作:}

相关操作
1、创建http服务:http.createServer((req, res) => {})

2获取请求行数据。
方法一:

// 由于http服务封装的req对象中没有query、params属性
// 故需要使用url模块将req.url进行构造,从中获取请求行数据
const u = require('url')
var url = u.parse(req.url)

// url中包含query属性,但其中数据的格式是字符串,故下行代码报错,无法获取
var ID  = url.query.userid

// 重新构造
var url = u.了parse(req.url, true)
// 这样格式就转化成了js对象

方法二:

// 原理同上,只是换用内置对象URL进行构造,
// 前缀'http...'是任意的。不过,出于业务考虑,应与客户端相同
var url = new URL(req.url, 'http://127.0.0.1:5500')

// URL对象中请求行数据封装在属性searchParams中,而不是query
// searchParams中数据的格式不是js对象,需要调用get()获取
var id = url.searchParams.get('id')

3获取请求体数据。

// req对象中同样没有body属性,需要使用'data'和'end'事件进行获取
var bodyData
req.on('data', temp => {
	// temp类型是String,故直接拼接
	bodyData += temp
})
req.on('end', () => {
	// bodyData是String,故如此无法获取,
	var id = bodyData.userid
})

我暂且也不知如何解析:String → js对象,大家自行补充了。

留言

color{orange}{留言:}

留言我未查阅资料的原因:(1)在上面express模块的示例中我提起过,不知为何req对象中没有body属性,故我无法测试;(2)express模块是基于http模块的封装,使用express模块搭建的js服务器更强大。

4设置响应头时,若有多个值,需使用数组

5res.write(str)需与res.end(str)连用。其中,res.write()用于附加响应。

6、当使用live-server打开html文件时,项目目录为当前html文件所在目录。

留言

color{brown}{留言:}

留言:这一点我还不太理解,至少我测试http://localhost:8081/1.jpg时仍然访问不到图片1.jpg与当前html文件同目录)。无妨,解决办法往下看。。。

6、关于静态资源无法访问问题

关于这个问题,相关描述在博文【05-Node.js—http模块】(转发)的第4.5项,找到其中“我们该如何解决?”那一段。
参考笔记三,P49.3。

悄咪咪的告诉大家,其实在当时学习http模块的时候,一开始我是没有注意这个细节的,因为觉得莫名其妙。“对路径进行判断”,我根本无法理解其中用意(自然也没有特别去测试),所以“撇开”了。
可后来想到博主既然会用那么一定篇幅来阐述这个问题,说明有一定重要性,于是按照博主的思路具体测试了一下,终于明白了,步入正题。。。

这个问题出现的场合:

  1. 上面http模块中项目根目录无效导致无法访问静态资源;
  2. js服务器响应html文件,文件内css、js、img静态资源无效或无法访问。(这就是那位博主所述的情况)

为了让大家充分理解,我逐步说明。。。

首先,若客户端请求的是图像文件,且正常响应,会无效吗?答案

N

O

color{red}{NO}

NO,除非响应有问题。比如:服务器未将图像的所有信息二进制)响应,那么此图像肯定无法正常显示。(一般不会有这种操作
然后,若客户端请求的是css、js等文件,同样正常响应,会无效吗?也是

N

O

color{red}{NO}

NO。这种情况下即便响应不完整,也不会无效,因为是一并解释。

那为何无效或无法访问

color{grey}{那为何无效或无法访问?}

那为何无效或无法访问
二种情况:

  1. 客户端处理响应时调用的是text(),而不是html()(以 jq 为例),以字符串的方式处理响应内容,自然无法识别标签(如:<script>),故无效;
  2. 找不到静态资源。

若是第一种情况,调用html()即可

那为何找不到资源?

color{grey}{那为何找不到资源?}

那为何找不到资源?
走到这一步,客户端已经可以正常处理响应,无论html、css、js还是img,故原因是:

  1. 访问路径有误;
  2. 服务器响应有误。

因为已经可以正常解析响应的html文件,即静态资源标签src属性有效。

在解析html文件时,会同时根据src属性的路径向服务器发起请求。

因此,这种情况下src必须是完整路径,如:http://127.0.0.1:8081/1.jpg,这样才有可能找到文件。

完整路径能找到静态资源吗?

color{grey}{完整路径能找到静态资源吗?}

完整路径能找到静态资源吗?
若js服务器由express模块搭建,只要配置好项目根目录(静态资源中间件),就可以直接找到静态资源。
若js服务器由http模块搭建,由于无法配置项目根目录,故只能由路由对完整路径进行处理,从而返回静态资源(那位博主说“对路径进行判断”就是这个意思)。

示例:

const httpSer = http.createServer((req, res) => {
    if (req.url == '/1.jpg') {
        fs.readFile(__dirname + url, (err, data) => {
            res.end(data)
        })
        return
    }
    var data = fs.readFileSync(__dirname + '/index.html')
    res.end(data)
})

O

K

P

e

r

f

e

c

t

!!

color{orange}{OK!Perfect!!}

OKPerfect!!http模块中项目根目录无效导致无法访问静态资源的问题也解决了。

7、关于通用设置

参考笔记三,P49.2/4。

“通用设置”指不同模块中业务相同的操作或函数。

  1. 设置状态码:res.statusCode()res.status()
  2. 设置状态码描述:res.statusMessage
  3. 设置响应体:res.write()res.end()res.send()
  4. 设置响应头:res.setHeader('标头', 值)res.set('标头', 值)
  5. 获取请求头:req.headers.refererreq.get('referer')
  6. (就列举这些哈,其他操作或函数不常用或者通过debug可以知晓,以名达意。)

注:

  1. “或”前操作或函数属http模块,后属express模块,且express模块是基于http模块的封装(express模块兼容http模块);
  2. res.end()res.send()都是设置响应体的末操作,故其后不能再做响应配置。

留言

本人的核心语言是Java,故有时倾向于以Java的思想进行阐述,这可能会给向前端发展的博友们的阅读带来不适。并且,由于本文相当于是我系统学习Node.js的笔记,也基于我的Java功底,所以有些阐述不会那么详细。
不过,Java作为一种强类型编程语言,我的阐述会很严谨,所以需要大家在阅读时多一点耐心。


本文持续更新中。。。

原文地址:https://blog.csdn.net/m0_69908381/article/details/134544523

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。

如若转载,请注明出处:http://www.7code.cn/show_3692.html

如若内容造成侵权/违法违规/事实不符,请联系代码007邮箱:suwngjj01@126.com进行投诉反馈,一经查实,立即删除

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注