nodejs

本文详细介绍了Node.js的基础知识,包括模块、包管理、内置模块(如http、event、fs、stream、crypto)以及Express框架的使用。讲解了Node.js的服务器创建、客户端请求、文件操作、事件驱动、服务端渲染、数据库操作、接口规范与业务分层、登录鉴权、文件上传管理、API文档生成工具ApIDoc和Koa2框架。此外,还涉及MongoDB的使用、Cookie&Session与JSON Web Token (JWT)的登录鉴权机制、以及WebSocket编程。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

目录

目录

Node.js

一、Node.js基础

1. 认识Node.js

01 nodejs的特性

02 浏览器环境vs node环境

2. 模块、包、commonJS

3. Npm&Yarn

01 npm的使用

02 package.json和package-lock.json

02 全局安装 nrm

03 yarn使用

5. 内置模块

01 http模块:作为服务器

(1)server对象

(2)server的事件

(3)server的request事件的参数req

(4)server的request事件的参数res

02 http模块:作为客户端

(1)http.request(options,callback)

(2)http.get(options,callback)

(3)http.ClientRequest

(4)http.ClientReponse

03 url模块

04 querystring模块,已被弃用,但仍然可用。

05 http模块补充

(1)接口:jsonp

(2) 跨域:CORS

(3) 模拟get

(4) 模拟post:服务器提交(攻击)

(5) 爬虫

05 event模块

06 fs文件操作模块

07 stream流模块

09 crypto

6. 路由

01 基础

02 获取参数

03 静态资源处理

二、Express

1.安装   npm install express --save

2.路由

(1)路由路径

(2)回调函数

4.中间件

(1)应用级中间件

(2)路由级中间件

(3)错误处理中间件

(4)内置的中间件

(5)第三方中间件

5. 获取请求参数

6.利用 Express 托管静态文件

7.服务端渲染(模板引擎)和服务端渲染

(1)服务端渲染模板

(2)客户端渲染

(3)node中间层

(4)服务端渲染(ssr)

(5)服务端渲染的实现方式

8.生成器

三、MongoDB

nodejs连接操作数据库

四、接口规范与业务分层

五、登录鉴权

1. Cookie&Session

2. JSON Web Token (JWT)

六、文件上传管理

七、APIDOC - API 文档生成工具

八、Koa2

九、Socket编程


Node.js

一、Node.js基础

1. 认识Node.js

Node.js是一个javascript运行环境。它让javascript可以开发后端程序,实现几乎其他后端语言实现的所有功能,可以与PHP、Java、Python、.NET、Ruby等后端语言平起平坐。

Nodejs是基于V8引擎,V8是Google发布的开源JavaScript引擎,本身就是用于Chrome浏览器的js解释部分,但是Ryan Dahl 这哥们,鬼才般的,把这个V8搬到了服务器上,用于做服务器的软件。

01 nodejs的特性

  • Nodejs语法完全是js语法,只要你懂js基础就可以学会Nodejs后端开发

  • NodeJs超强的高并发能力,实现高性能服务器

  • 开发周期短、开发成本低、学习成本低

02 浏览器环境vs node环境

Node.js 可以解析JS代码(没有浏览器安全级别的限制)提供很多系统级别的API,如:

  • 文件的读写 (File System)

    const fs = require('fs')
    
    fs.readFile('./ajax.png', 'utf-8', (err, content) => {
      console.log(content)
    })
  • 进程的管理 (Process)

    function main(argv) {
      console.log(argv)
    }
    
    main(process.argv.slice(2))
    
  • 网络通信 (HTTP/HTTPS)

    const http = require("http")
    
    http.createServer((req,res) => {
      res.writeHead(200, {
        "content-type": "text/plain"
      })
      res.write("hello nodejs")
      res.end()
    }).listen(3000)
    

2. 模块、包、commonJS

模块化简介

在node中,一个js文件就是一个模块。

每一个js文件中的js代码都是独立运行在一个函数中,而不是在全局作用域,所以一个模块中的变量和函数在其他模块中是不能访问的。

如果要让外部可以访问模块里面的方法或者属性,就必须在模块里面通过 exports 或者 module.exports 暴露属性或者方法。使用require引入。

exports:对象,该对象用来将变量和函数暴露到外部
require:函数,用来引入外部的模块
module:模块,代表我们当前的模块

通过exports只能用.的方式向外暴露自身变量和函数
module.exports既可以通过.的方式,也可以通过直接赋值的方式
    module.exports.xxx = xxx
    module.exports = {}
注意点:exports就是module的一个属性,即module.exports==exports 为true,但是exports不能通过对象的方式向外暴露属性

// m1.js
const name = 'gp19'
const sayName = () => {
  console.log(name)
}
// 接口暴露方法一:使用exports.xxx一个一个向外暴露
exports.name = name
exports.sayName = sayName
// 接口暴露方法二:使用module.exports把要暴露的内容放到一个对象中,批量向外暴露
module.exports = {
  name:name
  sayName: sayName
}
// 错误方法,不能exports一个对象
exports = {
  name:name
  sayName: sayName
}

// 其他文件通过require引入即可
// m2.js
 const m1 = require('./m1')
m1.say()

如果需要使用es6的模块化规范,需要在package.json中添加"type"="module"

3. Npm&Yarn

01 npm的使用

npm   init        //构建项目说明,生成 package.json文件
npm install 包名 –g  //或者uninstall,update,表示全局安装,卸载或者更新
npm install 包名 --save-dev //uninstall,update 表示全局安装,卸载或者更新 
//而参数--save 的作用是在项目下的package.json文件记录安装过的依赖包名称,当复制项目到另外的电脑上,只需运行命令: npm i 就能自动安装项目用到的依赖包
npm list -g //(不加-g,列举当前目录下的安装包)
npm info 包名 //(详细信息) npm info 包名 version(获取最新版本)
npm install md5@1//(安装指定版本)
npm outdated  //( 检查包是否已经过时)

02 package.json和package-lock.json

package.json 是在运行 “ npm init ”时生成的,主要记录项目依赖,有以下结构

  • name:项目名,也就是在使用npm init 初始化时取的名字,但是如果使用的是npm init -y 快速初始化的话,那这里的名字就是默认存放这个文件的文件名;
  • version:版本号;
  • private:希不希望授权别人以任何形式使用私有包或未发布的;
  • scripts-serve:是vue的项目启动简写配置;
  • scripts-build:是vue的打包操作简写配置;
  • dependencies:指定了项目运行时所依赖的模块;
  • devDependencies:指定项目开发时所需要的模块,也就是在项目开发时才用得上,一旦项目打包上线了,就将移除这里的第三方模块;

package-lock.json是在运行“npm install”时生成的一个文件,用于记录当前状态下项目中实际安装的各个package的版本号、模块下载地址、及这个模块又依赖了哪些依赖。

区别:

为什么有了package.json,还需要package-lock.json文件呢?

当项目中已有 package-lock.json 文件,在安装项目依赖时,将以该文件为主进行解析安装指定版本依赖包,而不是使用 package.json 来解析和安装模块。因为 package 只是指定的版本不够具体,而package-lock 为每个模块及其每个依赖项指定了版本,位置和完整性哈希,所以它每次创建的安装都是相同的。无论你使用什么设备,或者将来安装它都无关紧要,每次都应该给你相同的结果。

dependencies字段的说明:

"dependencies": { "md5": "^2.1.0"  } ^ 表示 如果 直接npm install 将会安md5的2.*.* 的最新版本
"dependencies": { "md5": "~2.1.0"  } ~ 表示 如果 直接npm install 将会安装md5 2.1.* 的最新版本
"dependencies": {    "md5": "*"  }  * 表示 如果 直接npm install 将会安装 md5的最新版本

02 全局安装 nrm

NRM (npm registry manager)是npm的镜像源管理工具,有时候国外资源太慢,使用这个就可以快速地在 npm 源间切换。

手动切换方法: npm config set registry https://registry.npm.taobao.org

安装 nrm

        在命令行执行命令,npm install -g nrm,全局安装nrm。

使用 nrm

        执行命令 nrm ls 查看可选的源。 其中,带*的是当前使用的源,上面的输出表明当前源是官方源。

切换 nrm

        如果要切换到taobao源,执行命令nrm use taobao。

测试速度

        你还可以通过 nrm test 测试相应源的响应时间。

03 yarn使用

npm install -g yarn
对比npm:
	速度超快: Yarn 缓存了每个下载过的包,所以再次使用时无需重复下载。 同时利用并行下载以最大化资源利用率,因此安装速度更快。
    超级安全: 在执行代码之前,Yarn 会通过算法校验每个安装包的完整性。

开始新项目
	yarn init 
添加依赖包
	yarn add [package] 
	yarn add [package]@[version] 
	yarn add [package] --dev 
升级依赖包
	 yarn upgrade [package]@[version] 
移除依赖包
	 yarn remove [package]
	 
安装项目的全部依赖
	 yarn install 

5. 内置模块

01 http模块:作为服务器

(1)server对象

// 引入http模块
const http = require('http');
// 创建本地服务器来从其接收数据
const server = http.createServer((req, res) => {
    //设置响应头信息
  res.writeHead(200, { 'Content-Type': 'application/json' });
    //设置响应体信息
  res.end(JSON.stringify({
    data: 'Hello World!'
  }));
});
//启动服务器,8000端口
server.listen(8000);
 
const http = require('http');
// 创建本地服务器来从其接收数据
const server = http.createServer();
// 监听请求事件,on方法为server绑定了request事件,只要客户端请求服务器的数据就会触发
server.on('request', (req, res) => {
    //req:请求对象,包含了与客户端相关的数据和属性
    //		req.url:客户端请求的 URL 地址
    //		req.method:客户端的 method 请求类型
    
    //设置 Content-Type 响应头,charset=utf-8防止中文乱码
    res.setHeader("Content-Type","text/html; charset=utf-8");
  	res.writeHead(200, { 'Content-Type': 'application/json' });
    //调用 res.end() 向客户端响应一些内容,调用后会关闭流。而res.write()也是响应一些内容,但不会关闭流
  	res.end(JSON.stringify({
    	data: 'Hello World!'
  	}));
});
//启动服务器
server.listen(8000);

第二段代码是通过直接创建一个Server对象,然后为其添加request事件监听。

而server.on()里面配置的写法和在http.createServer()里面直接配置的写法效果是一样的。

其实也就说createServer方法其实本质上也是为http.Server对象添加了一个request事件监听,这似乎更好理解了。

(2)server的事件

http.createServer创建的server对象是一个基于事件的服务器,它是继承自EventEmitter,事实上,nodejs中大部分模块都继承自EventEmitter,包括fs、net等模块,这也是为什么说nodejs基于事件驱动(关于EventEmitter的更多内容可以在官方api下的events模块找到),server提供的事件如下:

  • request:当客户端请求到来时,该事件被触发,提供两个参数req和res,表示请求和响应信息,是最常用的事件

  • connection:当TCP连接建立时,该事件被触发,提供一个参数socket,是net.Socket的实例

  • close:当服务器关闭时,触发事件(注意不是在用户断开连接时)

其中request事件是最常用的,而参数req和res分别是http.IncomingMessage和http.ServerResponse的实例

(3)server的request事件的参数req

req是http.IncomingMessage的实例,http.IncomingMessage是HTTP请求的信息,是后端开发者最关注的内容,一般由server的request事件发送,并作为第一个参数传递,其提供了3个事件,如下:

  • data:当请求体数据到来时,该事件被触发,该事件提供一个参数chunk,表示接受的数据,如果该事件没有被监听,则请求体会被抛弃,该事件可能会被调用多次(这与nodejs是异步的有关系)

  • end:当请求体数据传输完毕时,该事件会被触发,此后不会再有数据

  • close:用户当前请求结束时,该事件被触发,不同于end,如果用户强制终止了传输,也是用close

req实例的属性

img

(4)server的request事件的参数res

res是http.ServerResponse的实例,http.ServerResponse是返回给客户端的信息,决定了用户最终看到的内容,一般也由server的request事件发送,并作为第二个参数传递,它有三个重要的成员函数,用于返回响应头、响应内容以及结束请求

  • res.writeHead(statusCode,[heasers]):向请求的客户端发送响应头,该函数在一个请求中最多调用一次,如果不调用,则会自动生成一个响应头

  • res.write(data,[encoding]):想请求的客户端发送相应内容,data是一个buffer或者字符串,如果data是字符串,则需要制定编码方式,默认为utf-8,在res.end调用之前可以多次调用

  • res.end([data],[encoding]):结束响应,告知客户端所有发送已经结束,当所有要返回的内容发送完毕时,该函数必需被调用一次,两个可选参数与res.write()相同。如果不调用这个函数,客户端将用于处于等待状态。

02 http模块:作为客户端

http模块提供了两个函数http.request和http.get,功能是作为客户端向http服务器发起请求。

(1)http.request(options,callback)

options是一个类似关联数组的对象,表示请求的参数。常用的参数有host、port(默认为80)、method(默认为GET)、path(请求的相对于根的路径,默认是“/”,其中querystring应该包含在其中,例如/search?query=byvoid)、headers(请求头内容)。

callback作为回调函数,需要传递一个参数,为http.ClientResponse的实例,http.request返回一个http.ClientRequest的实例,一般叫req。

需要记住的是,如果我们使用http.request方法时需要利用返回的http.ClientRequest的实例调用end方法,没有调用end方法,服务器将不会收到信息。

(2)http.get(options,callback)

这个方法是http.request方法的简化版,唯一的区别是http.get自动将请求方法设为了GET请求,同时不需要手动调用req.end()。

因为http.get和http.request方法都是返回一个http.ClientRequest对象,所以我们来看一下这两个对象。

(3)http.ClientRequest

http.ClientRequest是由http.request或者是http.get返回产生的对象,表示一个已经产生而且正在进行中的HTPP请求,提供一个response事件,也就是我们使用http.get和http.request方法中的回调函数所绑定的对象,我们可以显式地绑定这个事件的监听函数

var http=require("http");
var options={    
    hostname:"cn.bing.com",
    port:80
}
var req=http.request(options);
req.on("response",function(res){
    res.setEncoding("utf-8");
    res.on("data",function(chunk){
        console.log(chunk.toString())
    });
    console.log(res.statusCode);
})
req.on("error",function(err){
    console.log(err.message);
});

​​​​​​​req.end();

http.ClientRequest也提供了write和end函数,用于向服务器发送请求体,通常用于POST、PUT等操作,所有写操作都必须调用end函数来通知服务器,否则请求无效。此外,这个对象还提供了abort()、setTimeout()等方法,具体可以参考文档

(4)http.ClientReponse

与http.ServerRequest相似,提供了三个事件,data、end、close,分别在数据到达、传输结束和连接结束时触发,其中data事件传递一个参数chunk,表示接受到的数据。其属性如下

74f4f9dbbc22129c90bcaacb888a49ac.png

此外,这个对象提供了几个特殊的函数

  • response。setEncoding([encoding]):设置默认的编码,当data事件被触发时,数据将会以encoding编码,默认值是null,也就是不编码,以buffer形式存储

  • response.pause():暂停结束数据和发送事件,方便实现下载功能

  • response.resume():从暂停的状态中恢复

03 url模块

1.   parse会解析url并转换成一个对象,方便我们对其进行操作。

url模块的以上方法已被弃用,替换为URL类,详见文档。

const url = require('url')
const urlString = 'https://www.baidu.com:443/ad/index.html?id=8&name=mouse#tag=110'
const parsedStr = url.parse(urlString)
//会生成一个对象
//Url {
//  protocol: null,
//  slashes: null,
//  auth: null,
//  host: null,
//  port: null,
//  hostname: null,
//  hash: null,
//  search: null,
//  query: null,
//  pathname: '/home',
//  path: '/home',
//  href: '/home'
//}
console.log(parsedStr)

2.   format可以将一个对象转换成url

const url = require('url')
const urlObject = {
  protocol: 'https:',
  slashes: true,
  auth: null,
  host: 'www.baidu.com:443',
  port: '443',
  hostname: 'www.baidu.com',
  hash: '#tag=110',
  search: '?id=8&name=mouse',
  query: { id: '8', name: 'mouse' },
  pathname: '/ad/index.html',
  path: '/ad/index.html?id=8&name=mouse'
}
const parsedObj = url.format(urlObject)
console.log(parsedObj)

02.3 resolve用于拼接url

const url = require('url')
var a = url.resolve('/one/two/three', 'four')  ( 注意最后加/ ,不加/的区别 )
//如果three后面加/那么就是在路径最后添加 	a='/one/two/three/four'   
//如果three后面不加/那么就是替换最后一个路径	a='/one/two/four'
var b = url.resolve('http://example.com/', '/one')
//b= 'http://example.com/one'
var c = url.resolve('http://example.com/one', '/two')
//c= 'http://example.com/two'

04 querystring模块,已被弃用,但仍然可用。

官方推荐URLSearchParams替代。或者使用querystringify插件

1. parse 把key=value&key=value形式的字符串转为对象

const querystring = require('querystring')
var qs = 'x=3&y=4'
var parsed = querystring.parse(qs)
console.log(parsed)
//{
//  x: '3',
//  y: '4'
//}

2. stringify 把对象转为key=value&key=value形式的字符串

const querystring = require('querystring')
var qo = {
  x: 3,
  y: 4
}
var parsed = querystring.stringify(qo)
console.log(parsed)
//x=3&y=4

3. escape/unescape

const querystring = require('querystring')
var str = 'id=3&city=北京&url=https://www.baidu.com'
var escaped = querystring.escape(str)
console.log(escaped)
//id%3D3%26city%3D%E5%8C%97%E4%BA%AC%26url%3Dhttps%3A%2F%2Fsiteproxy.ruqli.workers.dev%3A443%2Fhttps%2Fwww.baidu.com
const querystring = require('querystring')
var str = 'id%3D3%26city%3D%E5%8C%97%E4%BA%AC%26url%3Dhttps%3A%2F%2Fsiteproxy.ruqli.workers.dev%3A443%2Fhttps%2Fwww.baidu.com'
var unescaped = querystring.unescape(str)
console.log(unescaped)
//id=3&city=北京&url=https://www.baidu.com

05 http模块补充

(1)接口:jsonp

//后端代码
const http = require('http')
const url = require('url')

const app = http.createServer((req, res) => {
  let urlObj = url.parse(req.url, true)

  switch (urlObj.pathname) { 
    case '/api/user':
      res.end(`${urlObj.query.callback}({"name": "gp145"})`)
      break
    default:
      res.end('404.')
      break
  }
})
app.listen(8080, () => {
  console.log('localhost:8080')
})
前端的html文件中
<script>
        var oscript = document.createElement("script")
        oscript.src="http://localhost:3000/api/aaa?callback=test"

        document.body.appendChild(oscript)


        function test(obj){
            console.log(obj)
        }
</script>

(2) 跨域:CORS​​​​​​​

const http = require('http')
const url = require('url')
const querystring = require('querystring')

const app = http.createServer((req, res) => {
  let data = ''
  let urlObj = url.parse(req.url, true)

  res.writeHead(200, {
    'content-type': 'application/json;charset=utf-8',
      //只要在后端配置下面的语句就可以实现CORS解决跨域
    'Access-Control-Allow-Origin': '*'
  })
 req.on('data', (chunk) => {
    data += chunk
  })

  req.on('end', () => {
    responseResult(querystring.parse(data))
  })

  function responseResult(data) {
    switch (urlObj.pathname) {
      case '/api/login':
        res.end(JSON.stringify({
          message: data
        }))
        break
      default:
        res.end('404.')
        break
    }
  }
})

app.listen(8080, () => {
  console.log('localhost:8080')
})

(3) 模拟get

node也可以当作客户端,向服务器发信息,然后将信息传给前端。相当于做一个中间人。

var http = require('http')
var https = require('https')

// 1、接口 2、跨域
const server = http.createServer((request, response) => {
  var url = request.url.substr(1)

  var data = ''

  response.writeHeader(200, {
    'content-type': 'application/json;charset=utf-8',
    'Access-Control-Allow-Origin': '*'
  })
// http.get方法不需要在最后调用req.end()
  https.get(`https://m.lagou.com/listmore.json${url}`, (res) => {
 	//data事件,当服务端接收到数据时触发
    res.on('data', (chunk) => {
      data += chunk
    })
	//end事件数据接收完时触发
    res.on('end', () => {
      response.end(JSON.stringify({
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值