最近学习了基于前后端分离的开发模式,我前端使用Vue框架,后端使用nodejs开发API接口,写了一份后端学习Nodejs的笔记。
文章目录
1、Node.js简介
Node.js是javascript的后端运行环境
无法调用DOM,BOM等浏览器内置API
基于Express构建web应用
Electron框架构建跨平台桌面应用
restify框架构建API接口项目
2、http模块
不需要使用IIS、Apache等这些第三方web服务器软件。
基于Node.js提供的http模块,通过几行简单的代码,就能轻松手写一个服务器软件
3、服务器相关概念
IP地址格式:点分十进制(0,~255)之间的十进制整数
ping 网址
即可查看该网址服务器的IP地址
DNS:域名服务器
4、创建最基本的web服务器
http请求
//导入http模块
const http = require('http')
//创建web服务器实例
const server = http.createServer()
//为服务器实例绑定request事件,监听客户端的请求
server.on('request',function(req,res){
console.log('Someone visit our web server');
})
//启动服务器
server.listen(8080,function(){
console.log('server running at http://127.0.0.1:8080');
})
req响应对象
const http = require('http')
const server = http.createServer()
//req是请求对象,包含了与客户端相关的数据和属性
server.on('request', (req) => {
//客户端请求的url地址
const url = req.url
//客户端请求的method类型
const methods = req.method
const str = `Your request url is ${
url},method is ${
methods}`
console.log(str);
})
server.listen(80,() =>{
console.log('server running at http://127.0.0.1');
})
res响应对象
如果想访问与服务器相关的数据或属性
//调用res.end()方法,向客户端响应一些内容
res.end(str)
根据不同的url响应不同的html内容
const http = require('http');
const server = http.createServer();
server.on('request',(req,res) => {
let content = "<h1>404 not found!</h1>";
if(req.url == '/' || req.url == '/index.html'){
content = "<h1 style='color:red'>这里是主页</h1>"
} else if(req.url == '/about.html'){
content = "<h1 style='color:blue'>这里是关于页</h1>"
};
res.setHeader('Content-Type','text/html; charset=utf-8')
res.end(content)
});
server.listen(80,()=>{
console.log('服务器连接: http://127.0.0.1');
})
5、模块化
复用性、可维护性、按需加载
require()
方法加载需要的内置模块、用户自定义模块和第三方模块
向外共享模块作用域
1、module对象
存储和当前模块有关的信息
2、moudule.exports
默认为{}
require()方法永远以moudule.exports指向的对象为准
3、exports对象
exports与moudule.exports指向同一个对象
4、CommonJS规范
每个模块内部,module变量代表当前模块
6、Express基础
常见两种服务器:
Web网站服务器
API接口服务器
1、创建express
//导入
const express = require('express')
//创建
const app = express()
//启动
app.listen(80,() => {
console.log('express server running at http://127.0.0.1');
})
2、监听get与post请求、把内容相应给客户端
//导入
const express = require('express')
//创建
const app = express()
app.get('/user',(req,res) => {
res.send({
name:'zs', age: 20, gender:'男' })
})
app.post('/user',(req,res) => {
res.send('请求成功');
})
app.listen(80,() => {
console.log('express server running at http://127.0.0.1');
})
3、获取url中携带的查询参数、动态参数
app.get('/',(req, res) => {
console.log(req.query)
})
//这里:id是一个动态的参数,可加多个参数
app.get('/user/:id/:username',(req, res) => {
//req.params是动态匹配的url参数
console.log(req.params)
res.send(req.params)
})
4、托管静态资源
//将public目录下的文件对外开放访问
app.use(express.static('public'))
且该目录不会出现在访问路径中
调用多个express.static()函数先访问前面的
挂载路径前缀
//挂载路径前缀
app.use('/files',express.static('./public'))
5、nodemon
保存自动重启项目
安装
npm i -g nodemon
启动
nodemon app.js
7、Express路由
路由匹配,如果请求类型和请求的URL同时匹配成功,Express会将这次请求转给对应的函数处理
路由的使用
const express = require('express')
//创建 web服务器, 命名为app
const app = express()
//挂载路由
app.get('/',(req,res) => {
res.send('Hello World')})
app.post('/', (req, res) => {
res.send('Post Request')})
app.listen(80, () => {
console.log('running at http://127.0.0.1')})
不建议直接挂载到app上,创建路由模块
var express = require('express')
//创建 web服务器, 命名为app
var router = express.Router()
//挂载路由
router.get('/user/list',function (req,res) {
res.send('Get user list')})
router.post('/user/add', function(req, res) {
res.send('Add new user')})
module.exports = router
app.use()的作用就是来注册全局中间件
const userRouter = require('./router/user.js')
app.use(userRouter)
8、Express中间件
中间件函数的形参中,必须包含next参数。而路由处理函数中只包含req和res
1、定义中间件函数
const express = require('express')
const app = express()
const mw = function(req,res,next){
console.log('中间件被调用');
next()
}
app.listen(80,() => {
console.log('http://127.0.0.1');
})
2、全局中间件函数
const mw = function (req,res,next){
console.log('这是一个中间件')
next()
}
//全局生效的中间件
app.use(mw)
简化形式
app.use((req,res,next){
console.log('中间件')
next()
})
3、中间件的作用
可以在上游中间件统一为req或res对象添加自定义属性或方法,供下游的中间件或路由使用
const express = require("express");
const app = express()
app.use((req,res,next) => {
const timeNow = Date.now()
req.Nowtime = timeNow
next()
})
app.get('/',function(req,res){
res.send('Home page' + req.Nowtime)
})
app.get('/user',(req,res) => {
res.send('User page' + req.Nowtime)
})
app.listen(80,()=>{
console.log('http://127.0.0.1');
})
4、局部中间件
不使用app.use()
//局部中间件
const mw1 = (req,res,next) => {
console.log('调用了局部生效的中间件')
next()
}
//在路由里调用
app.get('/',mw1, (req,res) => {
res.send('Home')
})
定义多个
app.get('/',mw1,mw2,(req,res) => {
res.send('Home')})
app.get('/',[mw1,mw2],(req,res) => {
res.send('Home')})
5、错误级别中间件
必须注册在所有路由之后
app.get('/', (req,res) => {
throw new Error('服务器发生错误')
res.send('Home')
})
app.use((err,req,res,next) => {
console.log('发生了错误' + err.message)
res.send('Error:' + err.message)
})
6、内置中间件
express.static
express.json
解析JSON格式的请求数据
express.urlencoded
解析URL-encoded格式的请求数据
//json
const express = require("express");
const app = express()
app.use(express.json())
//通过express.urlencoded()解析表单 url-encoded格式的数据
app.use(express.urlencoded({
extenden:false}))
app.post('/user',(req,res)=>{
//req.body来接收客户端发送过来的请求体数据
console.log(req.body);
res.send('ok')
})
7、第三方中间件
npm i body-parser
const parserr = require('body-parser')
app.use(parser.urlencoded({
extended:false}))
8、自定义中间件
手动模拟一个类似于express.urlencoded这样的中间件,来解析post提交到服务器的表单数据
9、Express写接口
编写get接口
//在这里挂载对应的路由
router.get('/get',(req,res)=>{
const query = req.query
//响应处理的结果
res.send({
status: 0,
msg: 'GET 请求成功!', //描述
data: query //需要响应给客户端的数据
})
})
编写post接口
router.post('/post',(req,res) => {
const body = req.body
res.send({
status: 0,
msg: 'POST 请求成功!',
data: body
})
})
10、CORS跨域资源共享
npm install cors
//在路由之前配置cors解决跨域
const cors = require('cors')
app.use(cors())
1、作用
cors由一系列HTTP响应头组成,这些HTTP响应头决定浏览器是否组织前端JS代码跨域获取资源
浏览器的同源安全策略默认会阻止网页"跨域"获取资源。
主要在服务器端进行配置,客户端无需做任何额外配置
2、cors响应头部
响应头部中可携带Access-Control-Allow-Origin字段
Access-Control-Allow-Origin: <origin> | *
//其中origin参数的值指定了允许访问该资源的外域URL,通配符*表示任何域
例如
res.SetHeader('Access-Control-Allow-Origin','https://lnkjzm.cn')
默认情况CORS只支持客户端发起GET、POST、HEAD请求,若使用其他方式:
res.SetHeader('Access-Control-Allow-Methods','POST,GET,DELETE,HEAD')
3、简单请求和预检请求
简单请求:客户端与服务器之间只发生一次请求
预检请求:客户端与服务器之间发生两次请求,OPTION预检请求成功之后才会发生真正请求
11、WEB开发模式
1、概念
基于服务器渲染的传统Web开发模式
前端耗时少,有利于爬虫和SEO。
占用服务器端资源,开发效率低。
基于前后端分离的新型Web开发模式
依赖于Ajax技术。后端只负责提供API接口,前端使用Ajax调用接口
开发体验好,用户体验好,减轻了服务器端的渲染压力
不利于SEO
2、如何选择
企业级网站的主要功能是展示而没有复杂的交互,需使用服务器渲染
后台管理项目,需使用前后端分离
12、身份认证
服务端渲染推荐使用 Session认证机制
前后端分离推荐使用 JWT认证机制
1、Session认证机制
HTTP协议的无状态性,指客户端每次HTTP请求都是独立的,连续多个请求之间没有直接的关系,服务器不会主动保留每次HTTP请求的状态
Cookie是存储在用户浏览器中的一段不超过4KB的字符串
自动发送、域名独立、过期时限、4KB限制
2、Session中间件
npm i express-session
//配置Session中间件
const session = require('express-session')
app.use(
session({
secret: 'tianhai',
resave: false,
saveUninitialized: true
})
)
app.post('/api/login', (req,res)=> {
if(req.body.username !== 'admin' || req.body.password !== '000000'){
return res.send({
status: 1, msg: '登录失败'})
}
//将登录成功后的用户信息,保存到Session
req.session.user = req.body //用户的信息
req.session.islogin = true //用户的登录状态
res.send({
status:0,msg:'登录成功'})
})
//从Session中获取用户信息,响应给客户端
app.get('/api/username',(req,res) => {
if(!req.session.islogin){
return res.send({
status:1,msg:'fail'})
}
res.send({
status:0,
msg:'success',
username:req.session.user.username
})
})
//调用req.session.destroy()函数,清空服务器保存的session
app.post('/api/logout',(req,res) => {
req.session.destroy()
res.send({
status: 0,
msg:'退出登录成功'
})
})
2、JWT认证机制
Session需要配合Cookie才能实现,Cookie默认不支持跨域
当前端请求后端接口不存在跨域问题时,推荐使用Session
需要跨域请求后端接口时,使用JWT认证
JWT(JSON Web Token)工作原理
Token原理
JWT组成部分:Header.Payload.Signature(头部、有效荷载、签名)
其中Header和Signature是安全性相关,保证Token安全
Payload是真正的用户信息
JWT使用方式
把JWT放在HTTP请求头的Authorization字段中
Authorization: Bearer <token>
3、ExpressJWT
1、npm install jsonwebtoken express-jwt
2、jsonwebtoken用于生成JWT字符串
express-jwt用于将JWT字符串解析还原成JSON对象
const jwt = require('jsonwebtoken')
const expressJWT = require('express-jwt')
3、定义secret密钥
const secretKey = 'balabala'
4、在登录成功后生成JWT字符串
//调用jwt.sign()方法生成JWT字符串
//参数1用户的信息对象,参数2加密的密钥,参数3配置对象,可以配置token的有效期
//不能将密码加密到token字符串中
jwt.sign({
username: userinfo.username}, secretKey,{
expiresIn:'10h'})
res.send({
status:200,
message :'登录成功',
token: tokenStr
})
5、将JWT字符串还原为JSON对象,注册这个中间件
app.use(expressJWT({
secret: secretKey}).unless({
path:[/^\/api\//] }))
6、req.user获取用户信息
//只要配置成功了express-jwt 就可以把解析出来的用户信息挂载到req.user属性上
app.get('/admin/getinfo', function(req,res){
console.log(req.user)
res.send({
status:200,
message:'获取用户信息成功',
data:req.user
})
})
7、在最后面加上错误中间件
app.use((err,req,res,next) => {
if(err.name === 'UnauthorizedError') {
return res.send({
status: 401, message: '无效的token' })
}
//其他原因
res.send({
status:500,message:'未知错误'})
})
13、api_server
create database my_db_01
CREATE TABLE `my_db_01`.`ev_users` (
`id` INT NOT NULL,
`username` VARCHAR(255) NOT NULL,
`password` VARCHAR(255) NOT NULL,
`nickname` VARCHAR(255) NULL,
`email` VARCHAR(255) NULL,
`user_pic` TEXT NULL,
PRIMARY KEY (`id`),
UNIQUE INDEX `id_UNIQUE` (`id` ASC) VISIBLE,
UNIQUE INDEX `username_UNIQUE` (`username` ASC) VISIBLE)
ENGINE = InnoDB
DEFAULT CHARACTER SET = utf8
COMMENT = '用户信息表';
ALTER TABLE `my_db_01`.`ev_users`
CHANGE COLUMN `id` `id` INT NOT NULL AUTO_INCREMENT ;
INSERT INTO `my_db_01`.`ev_users` (`username`, `password`) VALUES ('admin', '000000');
使用 bcryptjs
对用户密码进行加密
- 加密密码无法被逆向破解
- 同一明文密码多次加密,得到的加密结果各不相同
1、安装bcryptjs
npm i [email protected]
2、导入
const bcrypt = require('bcryptjs')
3、调用 bcrypt.hashSync(明文密码,随机盐长度)
对用户的密码进行加密
userinfo.password = bcrypt.hashSync(userinfo.password, 10)