官方对Node.js的定义:Node.js是一个基于V8 JavaScript引擎的JavaScript运行时环境。
Node.js是在2009年诞生的,目前最新的版本是分别是LTS 16.15.1以及Current 18.4.0
可以在电脑上安装不同版本的Node,使用时,可以切换不同的版本。
// 03-运行代码传递参数.js
let a = 110;
let b = 220;
console.log(a+b);
console.log(process.argv[2]);
console.log(process.argv[3]);
console.log(process.argv[4]);
Node中给我们提供了一些全局对象,方便我们进行一些操作
// D:\录课\20220606\17-工程化之node\code\01-node初识
// console.log(__dirname);
// D:\录课\20220606\17-工程化之node\code\01-node初识\05-全局对象.js
// console.log(__filename);
// process 是进程的意思
// process.argv 得到运行JS文件,传递的参数
// console.log(process.argv);
// 定时器
// console.log("start...");
// // Immediate 立即的意思 宏任务
// setImmediate(()=>{
// console.log("setImmediate...");
// })
// console.log("end...");
console.log("start...");
setImmediate(()=>{
console.log("setImmediate...");
})
// 微任务
process.nextTick(()=>{
console.log("nextTick...");
})
console.log("end...");
global是一个全局对象,事实上前面我们提到的process、console、setTimeout等都有被放到global中
// 在浏览器环境中有一个window
// 在node环境中是没有window
// console.log(window);
// 在node环境有,也有一个全局对象,是global
// console.log(global);
// 为了统一,提出了一个叫globalThis关键字
// globalThis在node环境中代表global
// globalThis在浏览器环境中代表window
// console.log(globalThis === global); // true
var a = 110;
// 在node环境中定义的全局变量,并不会挂载到global上
console.log(global.a);
模块化(组件化)指的就是将一个大的功能拆分为一个一个小的模块,通过不同的模块的组合来实现一个大功能。
早期使用IIFE解决命名冲突问题,但也有新的问题
// a.js
// let name = "wc";
// let age = 18;
// function sum(){
// return 110
// }
let moduleA = (function(){
let name = "wc";
let age = 18;
function sum(){
return 110
}
return {
name,
age,
sum
}
}())
// b.js
// let name = "xq";
// let age = 28;
// function sum(){
// return 220
// }
let moduleB = (function(){
let name = "xq";
let age = 28;
function sum(){
return 220
}
return {
name,
age,
sum
}
}())
<!-- index.html -->
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Document</title>
</head>
<body>
<!-- 一个script标签就是一个代码段 -->
<script src="./a.js"></script>
<!-- b.js中的数据,污染了a.js中的数据 -->
<script src="./b.js"></script>
<script>
// 下面的代码段可以使用上在的代码段提供的数据
console.log(moduleA.name);
console.log(moduleA.age);
console.log(moduleB.name);
console.log(moduleB.age);
</script>
</body>
</html>
JavaScript社区为了解决上面的问题,涌现出一系列好用的规范,接下来我们就学习具有代表性的一些规范
CommonJS是一个规范,简称为CJS,Node是CommonJS在服务器端一个具有代表性的实现
CommonJS规范要求
exports导出
module.exports导出
require是一个函数,可以帮助我们引入一个文件(模块)中导出的对象
情况一:X是一个Node核心模块,比如path、http
情况二:X是以 ./ 或 …/ 或 /(根目录)开头的
情况三:直接是一个X(没有路径),并且X不是一个核心模块
CommonJS加载模块是同步的
ECMA推出自己的模块化系统,弥补了JavaScript没有模块化
ES Module模块采用export和import关键字来实现模块化
浏览器中演示ES6的模块化开发(需要在服务器端来测试)
<script src="./modules/foo.js" type="module"></script>
<script src="main.js" type="module"></script>
在开发和封装一个功能库时,通常我们希望将暴露的所有接口放到一个文件中,这样方便指定统一的接口规范,也方便阅读
默认导出(default export)
通过import加载一个模块,是不可以在其放到逻辑代码中的,ES Module在被JS引擎解析时,就必须知道它的依赖关系,这个时候js代码没有任何的运行,所以无法在进行类似于if判断中根据代码的执行情况
动态的来加载某一个模块
<!--
commonjs规范
1)只要是一个文件,都是一个模块,模块与模块之间天生隔绝,天生就不会相互污染。
2)你定义的模块中有数据,你想让别人使用,你需要把数据导出去。
3)你想用别人的模块,你需要导入
导出方案一、
exports.name=lc
exports.age=18
导出方案二、
moudle.exports.name=lc
moudle.exports.age=18
//在源码中,exports和moudle.exports指向了同一个对象,执行同一个堆
//exports=moudle.exports
导出方案三、(常用
moudle.exports={
name,age
}
导入
通过require导入
模块分三类
1)自定义模块,a.js
require('./a.js')必须以./或../开头
2)系统模块 node中提供好的模块,不需要下载,也叫核心模块
require("http") require("path") 不能以./或../打头
3)第三方模块
npm install jquery
第三方模块, 不能以./或../打头
-->
<!--
ES6Module规范
规范:
1)每一个文件,都是一个模块,模块与模块之间天生隔离
2)你想让别人使用你的数据,你就需要导出去
2)你想用别人的数据,你就是导进来
导出方案一、
通过export导出数据,导出的是标识符
export let name = lc;
export let age = 18;
导出方案二、批量导出
let name=lc;
let age=18;
//导出的是标识符列表
export{
name,age
}
导入
导入是通过import xx form xx
如果是自定义模块,也是必须以./或../打头
import 后面的 { }不是结构赋值,{}中放标识符
node使用的是commonjs规范
在node环境中直接使用es6规范就直接报错,node不认识
import { name, age } from "./a.js"
导入时起别名:import { name as username, age as userage } from "./a.js"
//使用时用别名
需要在浏览器运行,type='module'告诉浏览器开启模块化
需要使用live server,http:
导出方式三、默认导出
如果一个模块中,只有一个数据,可以使用默认导出
导出的话,是通过 export default
export default obj
默认导入
导入时,不需要写标志符列表{}
improt 后面写一个变量名,变量名是它导出的数据
变量名随便写
import xxx from "./a.js"
当多个文件时,可以建个index.js导出文件
export * from "./formatTime.js"
export * from "./formatMoney.js"
在main.js导入
import { formatTime, formatMoney, a, b, c } from "./tools/index.js"
注意:improt不能写在条件中,只能位于一个模块的最面的
可以用improt()方法
if(true){
let res= import("./a.js")
res.then(value=>{
console.log(1111)
}).
catch(err=>{
console.log(222)
})
}
//此处的await在最新的语法中,不需要写async
if(true){
try{
let res=await import("./a.js")
console.log(1111)
}catch(err=>{
console.log(222)
})
}
-->
----------------------------------
自己生成package.json文件:
通过npm init -y
后面安装的第三方依赖,都会记录到这个配置文件中,安装:npm i jquery
看一下配置文件: dependencies依赖的意思
"dependencies": {
"jqeury": "^0.0.1-security"
}
npm i jquery 这样安装,安装的是一个生产依赖
----------------------------------
依赖分两类:
1)生产依赖(安装到项目中):
开发项目过程中和项目上线,都需要的依赖
npm i jquery
npm i vue -S 指定是生产依赖
npm i react --save 指定是生产依赖
2)开发依赖(安装到项目中):
只在开发项目过程中使用的依赖
npm i less -D 指定开发依赖
npm i sass --save-dev 指定开发依赖
----------------------------------
全局安装一些依赖(全局依赖,工具),这些依赖是安装到电脑上的,当成一个工具使用的:
npm i nrm -g -g表示全局安装 说白了,就是安装一个工具
nrm工具:换源
上面通过npm i xxx 下载的依赖是从国外下载的,可能会被墙掉。nrm就是用来换下载源
nrm ls 查看都有哪些下载源
nrm use taobao 就可以把下载源切换到国内的taobao
----------------------------------
一个项目,有非常多的包,我们需要通过一个配置文件(package.json)来管理这些包
生成配置文件
{
"name": "1-npm", #包的名字(注意生成的包名不能使用中文,大写 !!! 不能使用 npm 作为包的名字)
"version": "1.0.0", #包的版本
"description": "", #包的描述
"main": "index.js", #包的入口文件
"scripts": { #脚本配置
"test": "echo \"Error: no test specified\" && exit 1"
},
"author": "", #作者
"license": "ISC" #版权声明
}
package.json常见的属性
依赖的版本管理(版本规范是X.Y.Z)
npm install(安装npm包分两种情况)
换源
npm其他命令之卸载某个依赖包
npm其他命令之清除缓存
npm更多命令
yarn 的相关命令
1) yarn --version 检测是否安装成功
2) yarn init 初始化,生成package.json
3) yarn global add package 全局安装
4) yarn global remove less 全局删除
5) yarn add package 局部安装
6) yarn add package --dev (相当于npm中的-D)
7) yarn remove package 删除局部包文件
8) yarn list 列出已经安装的包名 用的很少
9) yarn info packageName 获取包的有关信息 几乎不用
10) yarn 安装package.json中的所有依赖
不想修改npm原本从官方下载包的渠道,可以使用cnpm,并且将cnpm设置为淘宝的镜像
局部命令的执行
流程
URI
URL
URN
URL肯定是一个URI,URI并不一定是URL,也有可能是URN,URL与URN是URI的子集
URL作用
URL格式
1)DNS解析: 得到IP地址
2)IP地址: 找到服务器
3)端口: 找到对应的服务器 80 web服务 网页服务
BS架构
CS架构
服务器分类
域名分类
www: world Wide Web 万维网
DNS:Domain Name Server(域名服务器)
IP:Internet Protocol Address 互联网协议地址 IP地址
根据IP地址,就可以找到对应的服务器,服务器上可以提供N种服务器,你需要哪种服务呢?就需要根据端口号,来区分你需要哪种服务。
TCP:transmission Control Protocol 传输控制协议
建立连接 => 三次握手 (双方确认)
(1) 服务器啊, 我是浏览器, 我要和你建立连接
(2) 服务器看到了, 好的, 那么建立连接吧, 我准备好了, 你确定吗?
(3) 浏览器: 是的, 我确定!
连接就建立成功,三次握手 = 连接的发起 + 双方的确认
断开连接 => 四次挥手 (客气挽留)
(1) 一方A发起断开连接的消息
(2) 另一方B会确认收到断开的需求, 但是会要求等一等, 确认是否数据传输完毕
(3) B当确认完之后, 确实数据都传完了, 告知A, 连接可以断开了
(4) A确认收到消息, 告知B, 我要走了
HTTP:HyperText Transfer Protocol 超文本传输协议:
HTTPS: HyperText Transfer Protocol Secure 超文本传输安全协议。
HTTP和HTTPS的区别
HTTP是基于TCP通信协议来传递数据。通过一个可靠的连接来交换信息。在交换信息之前,客户端和服务器之间需要有规则。
HTTP通信包含两部分
HTTP请求报文组成部分
GET和POST区别(面试题)
GET过程
POST过程
在request对象的header中也包含很多有用的信息,客户端会默认传递过来一些信息
content-type是这次请求携带的数据的类型
content-length
keep-alive
响应的header中包括一些服务器给客户端的信息
CSDN响应头
同源策略:Same Origin Policy SOP 是浏览器的策略
有一个这样的域名:http://www.wangcai.com
* http://zhidao.wangcai.com 不同源
* http://www.wangcai.com:8080 不同源
* https://www.wangcai.com 不同源
* http://www.wangcai.com/phone/index.html 同源
* http://www.wangcai.com/phone/huawei/index.html 同源
总结
不受同源策略的限制
ajax受同源策略影响
Koa 是⼀个新的 web 框架,由 Express 幕后的原班⼈⻢打造, 致⼒于成为web 应⽤和 API 开发领域中的⼀个更⼩、更富有表现⼒、更健壮的基⽯。 通过利⽤ async 函数,Koa 帮你丢弃回调函数,并有⼒地增强错误处理。 Koa并没有捆绑任何中间件, ⽽是提供了⼀套优雅的⽅法,帮助您快速⽽愉快地编写服务端应⽤程序。
// 写后端代码,node代码,koa代码,使用的模块化是commonjs规范
// 框架:
// 学习框架,就是学习规则,学习规范,要求你需要在合适的位置写合适的代码
// 等待框架去调用
// 下面的一片代码的作用是为了创建一台服务器
// 我们写的这个服务器,位于我们自己的电脑上
// 这个服务器对应的IP是:127.0.0.1
// 这个IP对应的域名是loalhost
let Koa = require("koa")
// Koa是一个构造器 new Koa得到一个对象app
let app = new Koa();
// 中间件 use表示使用中间件 里面的回调函数会自动执行
app.use(async ctx => {
// ctx是上下文
// ctx.body = "hello kao"是给浏览器响应一个hello koa
ctx.body = "hello kao"
})
// 我这个服务器监听的端口是3000
// 如果动了服务器代码,需要重新启动服务
// 按ctrl不动,按两次C,就可以关掉服务器
app.listen(3000, () => {
console.log("server is running on 3000");
})
// 服务器运行来了,可以使用浏览器得服务器发送请求
// 有一个工具,叫postman,这个postman也可以向服务器发送请求
步骤
const Koa = require('koa');
const app = new Koa();
app.use(async ctx => {
ctx.body = 'Hello World';
});
app.listen(3000);
https://juejin.cn/post/7022870597860327437
中间件就是 匹配路由之前 或者 匹配路由完成后 做的一系列的操作,我们就可以把它叫做中间件。
// 中间件函数建议使用异步函数
// 参数1 ctx是上下文 是一个对象 这个对象中提供了很多API
// ctx.body = "hello koa" 用来给客户响应数据的
// 参数2 next 控制是否执行下一个中间件
const Koa = require('koa');
const app = new Koa();
// logger
app.use(async (ctx, next) => {
console.log(1);
await next();
console.log(5);
const rt = ctx.response.get('X-Response-Time ');
console.log(`${ctx.method} ${ctx.url} -${rt}`);
});
// x-response-time
app.use(async (ctx, next) => {
const start = Date.now();
console.log(2);
await next();
console.log(4);
const ms = Date.now() - start;
ctx.set('X-Response-Time', `${ms}ms`);
});
// response
app.use(async ctx => {
console.log(3);
ctx.status = 200; //设置响应状态码
ctx.type = 'html'; //等价于ctx.set('Content-Type ','text / html ')
ctx.body = 'Hello World'; //设置响应体
});
app.listen(3000); //语法糖 等同于http.createServer(app.callback()).listen(3000)
打印结果: 1 2 3 4 5
结论: 当请求开始时先通过X-Response-Time 和logger 中间件, 然后继续交给response 中间件,当⼀个中间件调⽤next() 则该函数暂停执⾏并将控制权传递给定义的下个中间件. 当在response 中间件执⾏后, 下游没有更多的中间件. 这个时候每个中间件恢复其上游⾏为
添加error全局事件侦听器
const Koa = require('koa');
const app = new Koa();
// app.use(async ctx => {
// ctx.body = 'hello world'
// })
// 触发错误 koa帮咱们做了处理
app.use(async (ctx, next) => {
throw new Error('未知错误');
})
//全局错误处理 后台打印
app.on('error', err => {
console.log('全局错误处理', err.message)
})
app.listen(3000);
const Koa = require('koa');
const app = new Koa();
// 错误处理中间件
app.use(async (ctx, next) => {
try {
await next();
} catch (error) {
// 给⽤户显示状态码
ctx.status = error.statusCode || error.status || 500;
//如果是ajax请求,返回的是json错误数据
ctx.type = 'json';
// 给⽤户显示
ctx.body = {
ok: 0,
message: error.message
};
// 系统⽇志
ctx.app.emit('error', error, ctx);
}
})
// 触发错误 koa帮咱们做了处理
app.use(async (ctx, next) => {
throw new Error('未知错误');
})
// response
//....
//全局错误处理 后台打印
app.on('error', err => {
console.log('全局错误处理', err.message)
})
app.listen(3000);
注意: 每次修改了服务器代码,都需要重新启动服务器,为了方便,可以全局安装nodemon。
const Koa = require('koa');
const app = new Koa();
const logger = require('koa-logger')
app.use(logger())
app.use(async (ctx, next) => {
throw new Error('未知错误');
})
// 全局的事件监听器
app.on('error', (err) => {
console.log('全局错误处理:', err.message, err.status, err.data)
})
app.listen(3000, () => {
console.log('3000端口被监听了~~')
})
const Koa = require('koa')
const onerror = require('koa-onerror')
const app = new Koa()
const logger = require('koa-logger')
app.use(logger())
onerror(app)
// koa的中间件
app.use(async (ctx, next) => {
// ctx.throw()相当于是一个中间件
ctx.throw(401, '未授权', {
data: '你瞅瞅'
})
// ctx.body = 'wc' //设置响应体
/*
等价
const err = new Error('未授权');
err.status = 401;
err.expose = true;
throw err;
*/
})
app.use(async (ctx) => {
ctx.body = '错误处理中间件'
})
// 全局的事件监听器
app.on('error', (err) => {
console.log('全局错误处理:', err.message, err.status, err.data)
})
app.listen(3000, () => {
console.log('3000端口被监听了~~')
})
koa-log4 ⽐较好⽤的node环境下处理⽇志处理的模块, koa-log4 在 log4js-node 的基础上做了⼀次包装,是 koa 的⼀个处理⽇志的中间件,此模块可以帮助你按照你配置的规则分叉⽇志消息。
操作步骤:
const path = require('path')
const log4js = require('koa-log4')
log4js.configure({
appenders: {
// 访问级别
access: {
type: 'dateFile',
// 生成文件的规则
pattern: '-yyyy-MM-dd.log',
// 文件名始终以日期区分
alwaysIncludePattern: true,
encoding: 'utf-8',
// 生成文件路径和文件名
filename: path.join(__dirname, 'logs', 'access')
},
application: {
type: 'dateFile',
pattern: '-yyyy-MM-dd.log',
alwaysIncludePattern: true,
encoding: 'utf-8',
filename: path.join(__dirname, 'logs', 'application')
},
out: {
type: 'console'
}
},
categories: {
default: {
appenders: ['out'],
level: 'info'
},
access: {
appenders: ['access'],
level: 'info'
},
application: {
appenders: ['application'],
level: 'WARN'
}
}
})
// // 记录所有访问级别的日志
// exports.accessLogger = () => log4js.koaLogger(log4js.getLogger('access'))
// // 记录所有应用级别的日志
// exports.logger = log4js.getLogger('application')
module.exports = {
// 记录所有访问级别的日志
accessLogger: () => log4js.koaLogger(log4js.getLogger('access')),
// 记录所有应用级别的日志
logger: log4js.getLogger('application')
}
修改对应的代码如下
const Koa = require('koa')
const onerror = require('koa-onerror')
const {
accessLogger,
logger
} = require('./logger')
const app = new Koa()
onerror(app)
app.use(accessLogger())
// koa的中间件
app.use(async (ctx, next) => {
// ctx.throw()相当于是一个中间件
ctx.throw(401, '未授权', {
data: '你瞅瞅'
})
// ctx.body = 'wc' //设置响应体
/*
等价
const err = new Error('未授权');
err.status = 401;
err.expose = true;
throw err;
*/
})
// app.use(async ctx => {
// ctx.body = 'Hello World';
// });
// 全局的事件监听器
app.on('error', (err) => {
logger.error(err)
})
app.listen(3000, () => {
console.log('3000端口被监听了~~')
})
router/index.js中代码如下:
// 首页模块
// 购物车模块
const Router = require("@koa/router")
const router = new Router();
// 配置路由
// get表示客户端,需要发送get请求
// 通过浏览器地址栏发送的请求,就是get请求
router.get("/home", (ctx, next) => {
ctx.body = "欢迎访问首页面"
})
router.get("/home/news", (ctx, next) => {
ctx.body = "首页面的新闻列表"
})
module.exports = router;
router/user.js中代码如下:
// 用户管理模块
// 导入路由模块 Router就是路由器的意思
const Router = require("@koa/router")
// 创建一个路由对象
const router = new Router();
// 把公共部分抽离出去
router.prefix("/user")
// 路由本身就是一个特殊的中间件
// 当客户端访问/user/list 由后面的中间件处理
router.get("/list", (ctx, next) => {
ctx.body = "用户列表"
})
// 路由:就是一个URL对应的一个响应(资源)
router.get("/add", (ctx, next) => {
ctx.body = "添加用户"
})
module.exports = router;
入口文件中导⼊并注册,如下:
const Koa = require("koa")
const user = require("./router/user.js")
const index = require("./router/index.js")
const app = new Koa();
// 注册用户模块路由
app.use(user.routes())
user.allowedMethods();
// 注册首页模块路由
app.use(index.routes())
index.allowedMethods();
app.listen(3000, () => {
console.log("server is running on 3000~");
})
let Router = require("@koa/router")
let router = new Router();
router.prefix("/user")
// 客户端给服务器发送请求,可以传递参数
// get传递参数有两种形式
//访问http://localhost:3000/user/3?name=wc
// 1)通过?传参 query传参 /user/del?name=wc&age18
// ? name = wc & age18 查询字符串,就是用来给服务器传递的参数
router.get("/del", (ctx, next) => {
// ctx.query 就可以获取到get请求,通过?传递过来的参数 或 通过query传递过过来的参数
console.log(ctx.query);
ctx.body = "删除用户"
})
// 还有一种传参的方式
//访问http://localhost:3000/users/3/1
// 1)params传参 /:id 表示动态参数 不是写死的
// /add/:id 叫动态路由
router.get("/add/:id/:score", (ctx, next) => {
console.log(ctx.params);
ctx.body = "增加用户"
})
module.exports = router;
let Router = require("@koa/router")
let router = new Router();
router.prefix("/user")
// router.post 用来处理前端的post请求
// 浏览器的地址栏,不能发送post请求,只能发送get请求
// 需要使用postman来测试
// 404 表示前端去访问后面某个路由,后面没有这个路由,得到404状态码
// post请求,如何给后端传递参数?
// post请求,需要把传递参数放到请求体中,在postman中不要选错了。
// 在postman中,选择body选项,在body选项下面,选择x-www-form-urlencoded,不要选错
// 其它的选择先不用管
// 如果得到post请求传递过来的参数?
// 答:安装 body-parser ,通过body-parser去获取数据,
// 安装:npm i koa - [email protected]
// 安装完后,还需要在入口中配置
// 配置完毕后,通过ctx.reqeust.body,可以获取前端传递过来的数据
router.post("/add", (ctx, next) => {
console.log(ctx.request.body);
ctx.body = "增加用户666"
})
module.exports = router;
// 入口 js
let Koa = require("koa")
let bodyparser = require("koa-bodyparser")
let user02 = require("./router/user02.js")
let app = new Koa();
// 使用bodyparser
app.use(bodyparser())
// 注册路由
app.use(user02.routes())
user02.allowedMethods();
app.listen(3000, () => {
console.log("server is running on 3000");
})
由于浏览器地址栏,只能发送get请求,要发送post请求,需要使用postman,如下:
let Router = require("@koa/router")
let router = new Router();
router.prefix("/user")
// 当用户直接访问/user/ 时,怎么办?
// 当访问/user/时,我们可以重定向现到/user/list 重新指定访问
// 状态码是200表示成功的请求
// 302表示重定向 不同的状态码有不同的含义
router.get("/", (ctx, next) => {
// 当访问/user/时,redirect表示重定向
ctx.redirect("/user/list")
})
router.get("/list", (ctx, next) => {
ctx.body = "用户列表"
})
// 如果服务器中的代码出问题,状态码是500
router.get("/add", (ctx, next) => {
// throw new Error("出错了")
ctx.body = "增加用户"
})
module.exports = router;
DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Documenttitle>
<link rel="stylesheet" href="./css/index.css">
head>
<body>
<h1>今天学习Koa,感觉非常爽~h1>
body>
html>
/* index.css */
h1 {
color: red;
}
配置
const Koa = require('koa')
const index = require('./router/index')
const users = require('./router/user')
const bodyParser = require('koa-bodyparser')
const static = require('koa-static');
const app = new Koa()
app.use(bodyParser());
app.use(static(__dirname + '/public'))
// 注册路由
app.use(index.routes())
index.allowedMethods()
app.use(users.routes())
users.allowedMethods()
app.listen(3000, () => {
console.log('3000端口被监听了~~')
})
// 把我们之前写的一个静态网页放到koa服务器中
let Koa = require("koa")
let static = require("koa-static")
let app = new Koa();
// [email protected] 托管静态资源
// 说白了,就是把前面学习的静态网页放到服务器
// 安装:npm i [email protected]
// __dirname 是node提供的全局变量,直接使用,表示不文件所有的目录
app.use(static(__dirname, "./public"))
app.listen(3000, () => {
console.log("server is running on 3000");
})
三种常⻅鉴权⽅式:Session/Cookie、Token+jwt、OAuth
cookie和session都是用来跟踪浏览器用户身份的会话方式
https://zhuanlan.zhihu.com/p/504924068
Http协议是⼀个⽆状态的协议,服务器不会知道到底是哪⼀台浏览器访问了它,因此需要⼀个标识⽤来让服务器区分不同的浏览器。cookie就是这个管理服务器与客户端之间状态的标识。cookie的原理是,浏览器第⼀次向服务器发送请求时,服务器在response头部设置Set-Cookie字段,浏览器收到响应就会设置cookie并存储,在下⼀次该浏览器向服务器发送请求时,就会在request头部⾃动带上Cookie字段,服务器端收到该cookie⽤以区分不同的浏览器。当然,这个cookie与某个⽤户的对应关系应该在第⼀次访问时就存在服务器端,这时就需要session了。
cookie的基本使用:
const Koa = require('koa');
const app = new Koa();
app.use(async ctx => {
// 需要在服务端种植一个cookie,种到了浏览器的时面
// 后面,每一次请求,浏览器都会带上这个cookie
// 默认情况下,会话结束,cookie就死了
// ctx.cookies.set("usrename", "wangcai");
// 活7天 设置cookie的生存时间
ctx.cookies.set("usrename", "wangcai", {
maxAge: 60000 * 60 * 24 * 7
});
// 获取浏览器带过来的cookie
console.log("获取cookie:", ctx.cookies.get("usrename"));
ctx.body = 'Hello World';
});
app.listen(3000);
使用cookie记录一次的访问时间,代码如下:
const Koa = require('koa');
const app = new Koa();
app.use(async ctx => {
// 获取cookie 第1次获取不到,值是und
let last = ctx.cookies.get("last")
// 第1次访问服务器 需要种植一个cookie
ctx.cookies.set("last", new Date().toLocaleString(), {
maxAge: 60000 * 60 * 24 * 356
});
if (last) {
ctx.body = `你上一次访问的时间是:${last}`
} else {
// 第1次访问
ctx.body = `这是你第1次访问本网站`
}
});
app.listen(3000);
session是会话的意思,浏览器第⼀次访问服务端,服务端就会创建⼀次会话,在会话中保存标识该浏览器的信息。它与cookie的区别就是session是缓存在服务端的,cookie 则是缓存在客户端,他们都由服务端⽣成,为了弥补Http协议⽆状态的缺陷。
基于session-cookie的身份认证
原理
安装和应⽤
let Koa = require("koa")
const session = require('koa-session');
let app = new Koa()
// 对cookie进行加密签名
app.keys = ['some secret hurr'];
// 对session进行配置,了解
const CONFIG = {
key: 'sid',
maxAge: 86400000, // sid的有效期,默认是1天
httpOnly: true, // 仅仅是服务器端修改cookie
signed: true, // 签名cookie
}
app.use(session(CONFIG, app))
app.use(async (ctx, next) => {
// 浏览器有一个默认行为,它会请求favicon.ico
// 服务器判断出请求favicon.ico,直接结束
if (ctx.path === '/favicon.ico') return;
// 第1次访问时,n是0,但是此时给session中存储了一个count,值是1
// 第2次访问时,n是1,但是此时给sesion中存储的count变成了2
let n = ctx.session.count || 0;
ctx.session.count = ++n
ctx.body = `这是你第${n}访问本网站~`
})
app.listen(3000, () => {
console.log("server is running on 3000");
})
1. 存储位置不同
- cookie的数据信息存放在客户端浏览器上。
- session的数据信息存放在服务器上
2. 存储容量不同
- 单个cookie保存的数据<=4KB,一个站点最多保存20个Cookie。
- 对于session来说并没有上限,但出于对服务器端的性能考虑,session内不要存放过多的东西,并且设置session删除机制。
3. 存储方式不同
- cookie中只能保管ASCII字符串,并需要通过编码方式存储为Unicode字符或者二进制数据。
- session中能够存储任何类型的数据,包括且不限于string,integer,list,map等。
4. 隐私策略不同
- cookie对客户端是可见的,别有用心的人可以分析存放在本地的cookie并进行cookie欺骗,所以它是不安全的。
- session存储在服务器上,对客户端是透明对,不存在敏感信息泄漏的风险。
5. 有效期上不同
- 开发可以通过设置cookie的属性,达到使cookie长期有效的效果。
- session依赖于名为JSESSIONID的cookie,而cookie JSESSIONID的过期时间默认为-1,只需关闭窗口该session就会失效,因而session不能达到长期有效的效果。
6. 服务器压力不同
- cookie保管在客户端,不占用服务器资源。对于并发用户十分多的网站,cookie是很好的选择。
- session是保管在服务器端的,每个用户都会产生一个session。假如并发访问的用户十分多,会产生十分多的session,耗费大量的内存。
7. 浏览器支持不同
- 假如客户端浏览器不支持cookie:
+ cookie是需要客户端浏览器支持的,假如客户端禁用了cookie,或者不支持cookie,则会话跟踪会失效。关于WAP上的应用,常规的cookie就派不上用场了。
+ 运用session需要使用URL地址重写的方式。一切用到session程序的URL都要进行URL地址重写,否则session会话跟踪还会失效。
- 假如客户端支持cookie:
+ cookie既能够设为本浏览器窗口以及子窗口内有效,也能够设为一切窗口内有效。
+ session只能在本窗口以及子窗口内有效。
8. 跨域支持上不同
- cookie支持跨域名访问。
- session不支持跨域名访问。
实现三个接口如下:
let Router = require("@koa/router")
let router = new Router();
router.prefix("/user")
// 登录接口
router.post("/login", (ctx, next) => {
// post请求,想去获取用户名和密码
// 配置koa-bodyparser
// 获取:ctx.request.body
let body = ctx.request.body;
// console.log("body:", body); // { user: '123', pwd: 'abc' }
// 把用户名存储在session区域中
// 现在,你要知道,在session区域中,存储了用户信息,用户信息仅仅有一个用户名
ctx.session.userinfo = body.user;
// 这个响应的是纯文本的字符串
// ctx.body = "登录成功"
ctx.body = {
ok: 1,
message: "登录成功"
}
})
// 获取用户信息
// 现在获取用户信息这个接口,需要身份认证
// 如果说有100个接口,都需要身份认证,可以把这一套逻辑抽离出去
// router.get("/getUser", (ctx, next) => {
// if (ctx.session.userinfo) {
// ctx.body = {
// ok: 1,
// message: "获取用户信息成功",
// userinfo: ctx.session.userinfo
// }
// } else {
// ctx.body = {
// ok: 0,
// message: "获取用户信息失败,请先登录",
// }
// }
// })
router.get("/getUser", require("../middleware/auth.js"), (ctx, next) => {
ctx.body = {
ok: 0,
message: "获取用户信息成功",
userinfo: ctx.session.userinfo
}
})
// 退出登录接口
router.post("/logout", (ctx, next) => {
if (ctx.session.userinfo) {
delete ctx.session.userinfo
}
ctx.body = {
ok: 1,
message: "退出登录成功"
}
})
// 获取个人收藏数据
// 要求,你访问此接口,也需要登录
router.get("/getLike", require("../middleware/auth.js"), (ctx, next) => {
ctx.body = {
ok: 0,
message: "获取个人收藏数据成功",
}
})
module.exports = router;
封装auth验证
// auth就是身份认证的意思
module.exports = async (ctx, next) => {
if (ctx.session.userinfo) {
await next()
} else {
ctx.body = {
ok: 0,
message:"身份认证失败"
}
}
}
入口JS配置如下:
let Koa = require("koa")
const session = require('koa-session');
let bodyparser = require("koa-bodyparser")
let user06 = require("./router/user06.js")
let app = new Koa()
// keys作用:用来对cookie进行签名
app.keys = ['some secret hurr'];
// 鉴权:
// 身份认证
// 登录接口,登录成功后,可以获取用户信息
// 退出登录,如果退出了,不能获取用户信息
// 有些接口,只能在登录的情况下,才能访问,如:获取用户信息也是一个接口
// 这就是所谓的身份认证,身份认证就是鉴权
// session鉴权 后面还会去学习JWT鉴权
// 对session进行配置,了解
const SESSON_CONFIG = {
key: 'wc', //设置cookie的key名字
maxAge: 86400000, //有效期,默认是一天
httpOnly: true, //仅服务端修改
signed: true, //签名cookie
}
app.use(session(CONFIG, app))
// 使用bodyparser
app.use(bodyparser())
// 注册路由
app.use(user06.routes())
user06.allowedMethods();
app.listen(3000, () => {
console.log("server is running on 3000");
})
postman测试如下:
当访问/getUser路由的时候需要守卫中间件 在项目根目录下,创建middleware/auth.js,代码如下:
module.exports = async (ctx, next) => {
if (ctx.session.userinfo) {
await next()
} else {
ctx.body = {
code: 401,
message: '未授权',
}
}
}
获取用户信息时,添加守卫,如下:
// 获取用户信息接口
router.get('/getUser', require('../middleware/auth'), async (ctx, next) => {
// console.log(ctx.session.userinfo);
ctx.body = {
ok: 1,
message: '获取数据成功',
a: 666,
userinfo: ctx.session.userinfo
}
})
安装
在user.js中配置如下:
let Router = require("@koa/router")
let jwt = require("jsonwebtoken")
const jwtAuth = require("koa-jwt")
let router = new Router();
router.prefix("/user")
// jwt+token鉴权
// 生成token需要指定一个密钥,需要保存好,不能让别知道
// 后面还需要验证token是否合法,也需要这个secret
const secret = "fdsfsadfas234412dffsadffasdf3"
// 登录接口
router.post("/login", (ctx, next) => {
// 1)得到用户信息 userinfo是用户名
let userinfo = ctx.request.body.user;
// 2)根据用户信息(用户名),生成token
let token = jwt.sign({
data: userinfo, // 最好不要放敏感数据
// 生成的令牌是有有效期 exp设置令牌的有效期
exp: Math.floor(Date.now() / 1000) + 60 * 60 // 1个小时后,token失效了
}, secret);
console.log("token:", token);
ctx.body = {
ok: 1,
message: "登录成功",
user: userinfo,
token: token
}
})
// 获取用户信息
// 获取用户信息,需要前端传递一个token
// token需要放在请求头
// 如果没有带信息,报错:Authentication Error
router.get("/getUser",
jwtAuth({ secret }),//对传入令牌进行校验
(ctx, next) => {
ctx.body = {
ok: 1,
message: "获取用户信息成功",
userinfo: ctx.state.user.data
}
})
// 退出登录接口
// 退出登录,保需要在前端把token销毁就OK
router.post("/logout", (ctx, next) => {
ctx.body = {
ok: 1,
message: "退出登录成功"
}
})
module.exports = router;
入口JS如下:
let Koa = require("koa")
let bodyparser = require("koa-bodyparser")
let user07 = require("./router/user07.js")
let app = new Koa()
// 使用bodyparser
app.use(bodyparser())
// 注册路由
app.use(user07.routes())
user07.allowedMethods();
app.listen(3000, () => {
console.log("server is running on 3000");
})
//鉴权
就是身份验证
登录匹配相关信息,匹配成功登录成功,登陆成功后就可以获取用户相关的信息
注销。
//cookie
1.客户端发送请求到服务端(比如登录请求)。
2.服务端响应客户端,并在响应头中设置 Set-Cookie。
3.客户端收到该请求后,如果服务器给了 Set-Cookie,那么下次浏览器就会在请求头中自动携带 cookie。
4.客户端发送其它请求,自动携带了 cookie,cookie 中携带有用户信息等。
5.服务端接收到请求,验证 cookie 信息
//session
1. 服务器在接受客户端⾸次访问时在服务器端创建seesion,然后保存seesion(我们可以将seesion保存在内存中,也可以保存在redis中,推荐使⽤后者),然后给这个session⽣成⼀个唯⼀的标识字符串,然后在响应头中种下这个唯⼀标识字符串。
2. 签名。这⼀步通过秘钥对sid进⾏签名处理,避免客户端修改sid。(⾮必需步骤)
3. 浏览器中收到请求响应的时候会解析响应头,然后将sid保存在本地cookie中,浏览器在下次http请求的 请求头中会带上该域名下的cookie信息。
4. 服务器在接受客户端请求时会去解析请求头cookie中的sid,然后根据这个sid去找服务器端保存的该客户端的session,然后判断该请求是否合法
B)说一下,session鉴权的流程?自己整理一下session鉴权流程
1.登录接口
post请求,获取用户信息,把用户名存在session中。
2.获取用户信息接口
验证,session有用户信息,就把该用户信息给浏览器
3.退出登录
把session用户名删掉。
4.入口js配置
//利用keys对cookie进行签名
// 对session进行配置
const SESSON_CONFIG = {
key: 'wc', //设置cookie的key名字
maxAge: 86400000, //有效期,默认是一天
httpOnly: true, //仅服务端修改
signed: true, //签名cookie
}
C)说一下,jwt鉴权的流程?自己整理一下jwt鉴权流程
1.登录接口
1)得到用户信息
let userinfo = ctx.request.body.user;
2)服务器根据用户名生成相应令牌jsonwebtoken
let token = jwt.sign({
data: userinfo, // 最好不要放敏感数据
exp: Math.floor(Date.now() / 1000) + 60 * 60 // 1个小时后,token失效了
}, secret);
3)响应给前端一个token,把token放在浏览器数据库里面
2.获取信息
token需要放在请求头中,去后台验证是否合法,合法解析出对应信息给前端
3.退出登录
在前端把token销毁
4.入口js
Representational State Transfer翻译过来是"表现层状态转化”, 它是一种互联网软件的架构原则。因为符合REST风格的Web API设计,就称它为Restful API,RESTful是目前最流行的API规范,适用于Web接口规范的设计。让接口易读,且含义清晰。本文将介绍如何设计易于理解和使用的API,并且借助Docker api的实践说明。
动词+宾语
正确的例子
- GET /zoos: 列出所有动物园
- POST /zoos: 新建一个动物园
- GET /zoos/id: 获取某个指定动物园的信息
- PUT /zoos/id: 更新某个指定动物园的信息(提供该动物园的全部信息)
- PATCH /zoos/id: 更新某个指定动物园的信息(提供该动物园的部分信息)
- DELETE/zoos/id: 删除某个动物园
- GET /zoos/id/animals: 列出某个指定动物园的所有动物
- DELETE/zoos/id/animals/id: 删除某个指定动物园的指定动物
有些客户端只能使用GET和POST这两种方法。服务器必须接受POST模拟其他三个方法(PUT、PATCH、DELETE)。这时,客户端发出的 HTTP请求,要加上X-HTTP-Method-Override属性,告诉服务器应该使用哪一个动词,覆盖POST 方法。
就是API的url,是HTTP动词作用的对象,所以应该是名词。例如/books这个URL就是正确的,而下面的URL不是名词,都是错误的写法。如下:
GET / getAllUsers ? name = wc
POST / createUser
POST / deleteUSer
URL是名词,那么是使用复数还是单数? 没有统一的规定,但是我们通常操作的数据多数是一个集合,比如GET/books , 所以我们就使用复数。统一规范,建议都使用复数URL,比如获取id =2的书GET /books/2要好于GET/book/2 。
有时候我们要操作的资源可能是有多个层级,因此很容易写多级URL,比如获取某个作者某种分类的文章。
GET / authors / 2 / categories / 2 // 获取作者ID=2获取分类=2的文章
这种URL不利于拓展,语义也不清晰更好的方式就是除了第一级,其他级别都是通过查询字符串表达。正确方式:
GET / authors / 2 ? categories = 2
查询已发布的文章
错误写法: GET / artichels / published
正确写法: GET / artichels ? published = true
状态码如果记录数量很多,服务器不可能都将它们返回给用户。API应该提供参数, 过滤返回结果。
下面是一些常见的参数。
参数的设计允许存在冗余,即允许API路径和URL参数偶尔有重复。比如,GET /zoo/ID/animals 与GET /animals?zoo-id=ID的含义是相同的。推荐后者,避免出现多级URL。
客户端的请求,服务求都必须响应,包含HTTP状态码和数据。
HTTP状态码就是一个三位数,分成五个类别。
1xx︰相关信息
2xx: 操作成功,200状态码表示操作成功,但是不同的方法可以返回更精确的状态码。
3xx: 重定向
4xx︰客户端错误,4xx状态码表示客户端错误,主要有下面几种。
5xx: 服务器错误,5xx状态码表示服务端错误。一般来说,API不会向用户透露服务器的详细信息,所以只要两个状态码就够了。
不要返回纯文本 API返回的数据格式,不应该是纯文本,而应该是一个JSON对象,因为这样才能返回标准的结构化数据。所以,服务器回应的HTTP头的Content-Type属性要设为application/json 。客户端请求时,也要明确告诉服务器,可以接受JSON格式,即请求的HTTP头的ACCEPT属性也要设成 application/json。下面是一个例子。
发生错误的时候,不要返回200状态码 有一种不恰当的做法是,即使发生错误,也返回200状态码,把错误信息放在数据体里面,就像下面这样。
HTTP / 1.1200 OK
Content - Type: application / json
{
"status": "fail",
"msg": "错误"
}
上面代码中,解析数据体以后,才能得知操作失败。这种做法实际上取消了状态码,这是完全不可取的。正确的做法是,状态码反映发生的错误,具体的错误信息放在数据体里面返回。下面是正确方式:
HTTP / 1.1 400 Bad Request
Content - Type: application / json
{
"status": "fail",
"msg": "错误"
}
// router/user.js
let Router = require("@koa/router")
let captcha = require("trek-captcha")
let router = new Router();
router.prefix("/user")
// 当成数据库中的数据
let users = [
{ id: 1, name: "malu", age: 18 },
{ id: 2, name: "wangcai", age: 28 },
];
// 如果前端没有传递过来了一个姓名,获取所有用户信息
// 或
// 如果前端传递过来了一个姓名,那就根据姓名获取某个用户的信息
// get请求传参:1)query 2)params
router.get("/", async (ctx, next) => {
let name = ctx.query.name;
let data = users;
if (name) {
data = users.filter(u => u.name == name)
}
ctx.body = {
ok: 1,
data
}
})
// 根据ID获取某个用户的信息
// get请求传参:1)query 2)params
router.get("/:id", ctx => {
let id = ctx.params.id;
// console.log(id);
// 前端传递给后端的数据是一个字符串
// console.log(typeof id);
let data = users;
if (id) {
// +id +"001" +就是把一个string类型转成number类型
data = users.filter(u => u.id === +id)
}
ctx.body = {
ok: 1,
data
}
})
// 添加用户
router.post("/", ctx => {
let name = ctx.request.body.username;
let age = ctx.request.body.userage;
let id = users.length + 1;
let user = { id, name, age };
users.push(user);
ctx.body = {
ok: 1,
msg: "添加用户成功"
}
})
// 删除某个用户
// /:id params传参
router.delete("/:id", ctx => {
let id = +ctx.params.id;
// 根据id找到当前用户的索引
let idx = users.findIndex(u => u.id === id);
// 没有找到就返回-1
if (idx > -1) {
users.splice(idx, 1)
}
ctx.body = {
ok: 1,
msg: "删除用户成功"
}
})
// 修改用户
router.put("/", ctx => {
// put请求传递的参数获取方式和post是一样的
let id = +ctx.request.body.id;
let idx = users.findIndex(u => u.id === id);
if (idx > -1) {
users[idx].name = ctx.request.body.username
users[idx].age = ctx.request.body.userage
}
ctx.body = {
ok: 1,
msg: "修改用户成功"
}
})
// 不管是前端,还是后端
// 前期写业务:增删改查
// CRUD
module.exports = router;
入口JS如下:
const Koa = require('koa')
const index = require('./router/index')
const users = require('./router/user')
const bodyParser = require('koa-bodyparser')
const app = new Koa()
app.use(bodyParser());
// 注册路由
app.use(index.routes())
index.allowedMethods()
app.use(users.routes())
users.allowedMethods()
app.listen(3000, () => {
console.log('3000端口被监听了~~')
})
安装: npm i koa2-cors
const koa = require('koa');
const cors = require('koa2-cors');
const app = new koa();
app.use(cors());
安装: npm i [email protected]
// router/index.js
const Router = require('@koa/router');
const multer = require('koa-multer')
// 配置 磁盘存储
// diskStorage dist是硬盘的意思 Storage是存储的意思
const storage = multer.diskStorage({
// 文件保存路径
destination: function(req, file, cb) {
cb(null, 'public/upload')
},
// 修改文件名称
filename: function(req, file, cb) {
// 获取后缀名
const filterFormat = file.originalname.split('.')
cb(null, Date.now() + '.' + filterFormat[filterFormat.length - 1])
},
})
//加载配置
const upload = multer({
storage
})
const router = new Router();
router.prefix('/user')
router.post('/upload', upload.single('avatar'), (ctx, next) => {
// file 是avatar文件的信息
// ctx.req.body 文本域的数据 如果存在
console.log(ctx.req.file.filename)
ctx.body = {
ok: 1,
message: "上传成功"
}
})
module.exports = router;
安装: npm i [email protected]
// router/index.js
const Router = require('@koa/router');
const bouncer = require('koa-bouncer')
const router = new Router();
router.prefix('/user')
// 表单验证
router.post('/', async (ctx, next) => {
console.log(ctx.request.body)
// ctx.request.body {uname,pwd1,pwd2}
try {
/*
ctx.validateBody('username')
.required('Username is required')
.isString()
.trim()
.isLength(3, 15, 'Username must be 3-15 chars');
*/
ctx
.validateBody('uname')
.required('用户名是必须的')
.isString()
.trim()
.isLength(4, 8, '用户名必须是4~8位')
ctx
.validateBody('email')
.optional()
.isString()
.trim()
.isEmail('非法的邮箱格式')
ctx
.validateBody('pwd1')
.required('密码是必填项')
.isString()
.trim()
.isLength(6, 16, '密码必须是6~16位')
ctx
.validateBody('pwd2')
.required('密码是必填项')
.isString()
.trim()
.eq(ctx.vals.pwd1, '两次密码不一致')
console.log(ctx.vals)
ctx.body = {
code: 1,
}
} catch (error) {
// 校验异常
if (error instanceof bouncer.ValidationError) {
console.log(error)
ctx.status = 400
ctx.body = {
code: 400,
message: '校验失败:' + error.message,
}
return
}
throw error
}
})
module.exports = router;
入口JS如下:
const Koa = require('koa')
const index = require('./router/index')
const users = require('./router/user')
const bodyParser = require('koa-bodyparser')
const static = require('koa-static');
const bouncer = require('koa-bouncer');
const app = new Koa()
// 使用校验中间件
app.use(bouncer.middleware());
app.use(bodyParser());
app.use(static(__dirname + '/public'))
// 注册路由
app.use(index.routes())
index.allowedMethods()
app.use(users.routes())
users.allowedMethods()
app.listen(3000, () => {
console.log('3000端口被监听了~~')
})
安装: npm i [email protected]
// router/index.js
const Router = require('@koa/router');
const bouncer = require('koa-bouncer')
const catpcha = require('trek-captcha')
const router = new Router();
router.prefix('/user')
// 图形验证码
router.get('/captcha', async (ctx, next) => {
const {
token,
buffer
} = await catpcha({
size: 4
});
console.log(data); //{buffer:图片,token:'dbpwt'}
// ctx.state.bufferToken = token
//token的作用 前端输入完验证码与此时的token做对比
ctx.body = buffer;
})
module.exports = router;
入口JS:
const Koa = require('koa')
const index = require('./router/index')
const users = require('./router/user')
const bodyParser = require('koa-bodyparser')
const static = require('koa-static');
const bouncer = require('koa-bouncer');
const app = new Koa()
app.use(bouncer.middleware());
app.use(bodyParser());
app.use(static(__dirname + '/public'))
// 注册路由
app.use(index.routes())
index.allowedMethods()
app.use(users.routes())
users.allowedMethods()
app.listen(3000, () => {
console.log('3000端口被监听了~~')
})
在postman中测试如下:
使用img标签,使用验证码如下:
<img src="http://localhost:3000/user/captcha" alt="">
//dependencies属性是指定无论开发环境还是生成环境都需要依赖的包
//devDependencies生产依赖
总结安装依赖
npm init –y 生成配置文件
npm i koa@2.13.4 -S koa
npm i @koa/router@10.1.1 路由中间件koa-router
npm i koa-bodyparser@4.3.0 post请求
npm i koa-session@6.2.0 session
npm i koa-jwt@4.0.3 jwt中间件
npm i jsonwebtoken@8.5.1 ⽤于⽣成token下发给浏览器
npm i koa-static@5.0.0 静态资源托管
npm i koa2-cors 解决跨域
npm i koa-multer@1.0.2 文件上传
npm i koa-bouncer@6.0.0 表单验证
npm i trek-captcha@0.4.0 图形验证码
npm i koa-logger@3.2.1 koa-logger处理⽇志
npm i koa-onerror@4.2.0 koa-erros处理错误
npm i koa-log4@2.3.2 koa-log4处理⽇志
npm i express@4.17.3 express
npm i mongodb@4.4.0 mongodb
npm i mongoose@6.2.4 mongoose
monogodb原⽣驱动( npm i )
"dependencies": {
"express": "^4.17.3",
"mongodb": "^4.4.0",
"mongoose": "^6.2.4",
"sequelize": "^6.17.0"
}
班级学生管理(npm i)
"dependencies": {
"@koa/router": "^10.1.1",
"express": "^4.17.3",
"koa": "^2.13.4",
"koa-bodyparser": "^4.3.0",
"koa-static": "^5.0.0",
"mongodb": "^4.4.0",
"mongoose": "^6.2.4",
"sequelize": "^6.17.0"
}
后台管理系统(依赖)
bcrypt 对数据进⾏加盐
下载 bcrypt 包时,请先全局下载 node-gyp 包,npm i node-gyp -g
jsonwebtoken JSON WEB令牌实现
koa-jwt Koa JWT身份验证中间件
koa-bouncer Koa路由的参数验证库
basic-auth Nodejs基本身份验证解析器
mongoose MongoDB DOM框架
redis ⾼性能Redis客户端。
moment 解析,验证,操作和显示⽇期时间库
本地访问地址
localhost
127.0.0.1