基于前后端分离的Nodejs的Express框架开发API接口

	最近学习了基于前后端分离的开发模式,我前端使用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)

猜你喜欢

转载自blog.csdn.net/tianhai12/article/details/123856659