node

一、node.js

(一)创建web服务器

  1. 具体代码

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    const http = require('http');     // 1.引用http系统模块
    const app = http.createServer() ; //2. 创建web服务器
    //3.监听浏览器的请求
    app.on('request', (req, res) => {
    res.end('<h1>hi, user</h1>'); // req 请求对象 res响应对象
    });
    //4.启动服务绑定端口
    app.listen(3100, () => { console.log('启动成功'); });


    简写:
    require('http').createServer().on('request', (req, res) => { res.end('hello world')}).listen(4000)

(二) HTTP协议

  1. 请求报文

    1
    2
    3
    4
    5
    app.on('request', (req, res) => {
    req.headers // 获取请求报文
    req.url // 获取请求地址
    req.method // 获取请求方法
    });
  2. 响应报文

    1
    2
    3
    4
    5
    6
    app.on('request', (req, res) => {
    // 设置响应报文
    res.writeHead(200, {
    'Content-Type': 'text/html;charset=utf8‘
    });
    });

(三)、HTTP请求与响应处理

  1. get请求参数

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    const http = require('http');
    // 导入url系统模块 用于处理url地址
    const url = require('url');
    const app = http.createServer();
    app.on('request', (req, res) => {
    // 将url路径的各个部分解析出来并返回对象
    // true 代表将参数解析为对象格式
    let { query, pathname } = url.parse(req.url, true);
    });
    app.listen(3000);
  2. post请求参数

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    // 导入系统模块querystring 用于将HTTP参数转换为对象格式
    const querystring = require('querystring');
    app.on('request', (req, res) => {
    let str = '';
    // 监听参数传输事件
    req.on('data', (chunk) => str += chunk;);
    // 监听参数传输完毕事件
    req.on('end', () => {
    console.log(querystring.parse(str));
    });
    });
  3. 路由

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    // 引入router模块
    const getRouter = require('router');
    // 获取路由对象
    const router = getRouter();
    // 学生信息集合
    const Student = require('../model/user');
    // 引入模板引擎
    const template = require('art-template');
    // 引入querystring模块
    const querystring = require('querystring');

    // 呈递学生档案信息页面
    router.get('/add', (req, res) => {
    let html = template('index.art', {});
    res.end(html);
    })

    // 呈递学生档案信息列表页面
    router.get('/list', async (req, res) =>{
    // 查询学生信息
    let students = await Student.find();
    console.log(students);
    let html = template('list.art', {
    students: students
    })
    res.end(html)
    })
    // 实现学生信息添加功能路由
    router.post('/add', (req, res) => {
    // 接收post请求参数
    let formData = '';
    req.on('data', param => {
    formData += param;
    });
    req.on('end', async () => {
    await Student.create(querystring.parse(formData))
    res.writeHead(301, {
    Location: '/list'
    });
    res.end()
    })
    });

    module.exports = router;

二、Express框架

(一)创建

  1. 具体使用

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    // 引入express框架
    const express = require('express');
    const fs = require('fs');
    const promisify = require('util').promisify;
    const readFile = promisify(fs.readFile);
    // 创建网站服务器
    const app = express();

    app.get('/request' , (req, res, next) => {
    // send()
    // 1. send方法内部会检测响应内容的类型
    // 2. send方法会自动设置http状态码
    // 3. send方法会帮我们自动设置响应的内容类型及编码
    req.name = "张三";
    res.send('Hello. Express');
    })

    // 当客户端访问/admin请求的时候走当前中间件
    app.use('/admin', (err, req, res, next) => {
    // 用户没有登录
    let isLogin = true;
    // 如果用户登录
    if (isLogin) {
    // 让请求继续向下执行
    next()
    }else {
    // 如果用户没有登录 直接对客户端做出响应
    res.send('您还没有登录 不能访问/admin这个页面')
    // 或者
    // 为客户端响应404状态码以及提示信息
    res.status(404).send('您还没有登录 不能访问/admin这个页面')
    }

    res.status(500).send(err.message);
    })

    app.get('/index', async (req, res, next) => {
    try {
    await readFile('./aaa.js')
    }catch (ex) {
    next(ex);
    }
    })

    // 接收所有请求的中间件 错误处理中间
    app.use((err, req, res, next) => {
    res.status(500).send(err.message);
    })

    // 监听端口
    app.listen(3000);
    console.log('网站服务器启动成功');
  2. 路由

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    // 引入express框架
    const express = require('express');
    // 创建网站服务器
    const app = express();
    // 创建路由对象
    const home = express.Router();
    const admin = require('./route/admin');

    // 为路由对象匹配请求路径
    app.use('/home', home);
    app.use('/admin', admin);

    // 创建二级路由
    home.get('/index', (req, res) => {
    res.send('欢迎来到博客首页页面')
    })

    // 端口监听
    app.listen(3000);
  3. 请求参数处理

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    // 引入express框架
    const express = require('express');
    const bodyParser = require('body-parser');
    // 创建网站服务器
    const app = express();
    // 拦截所有请求
    // extended: false 方法内部使用querystring模块处理请求参数的格式
    // extended: true 方法内部使用第三方模块qs处理请求参数的格式
    app.use(bodyParser.urlencoded({extended: false}))

    app.post('/add', (req, res) => {
    // 接收post请求参数
    res.send(req.body)
    })

    app.get('/index/:id/:name/:age', (req, res) => {
    // 接收post请求参数
    res.send(req.params)
    })

    // 端口监听
    app.listen(3000);
  4. 实现静态资源访问功能

    1
    2
    3
    4
    5
    6
    7
    8
    9
    const express = require('express');
    const path = require('path');
    const app = express();

    // 实现静态资源访问功能
    app.use('/static',express.static(path.join(__dirname, 'public')))

    // 端口监听
    app.listen(3000);
  5. 模板引擎artTemplate(第三方模块)

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    const express = require('express');
    const path = require('path');
    const app = express();

    // 模板配置
    // 1.告诉express框架使用什么模板引擎渲染什么后缀的模板文件
    // 1.模板后缀
    // 2.使用的模板名
    app.engine('art', require('express-art-template'))
    // 2.告诉express框架模板存放的位置是什么
    app.set('views', path.join(__dirname, 'views'))
    // 3.告诉express框架模板的默认后缀是什么
    app.set('view engine', 'art');

    app.get('/index', (req, res) => {
    // 1. 拼接模板路径
    // 2. 拼接模板后缀
    // 3. 哪一个模板和哪一个数据进行拼接
    // 4. 将拼接结果响应给了客户端
    res.render('index', {
    msg: 'message'
    })
    });

    app.get('/list', (req, res) => {
    res.render('list', {
    msg: 'list page'
    })
    })


    // 端口监听
    app.listen(3000);

(二)使用

  1. 数据库连接

    1
    2
    3
    4
    5
    6
    // 引入mongoose第三方模块
    const mongoose = require('mongoose');
    // 连接数据库
    mongoose.connect('mongodb://localhost/blog', {useNewUrlParser: true })
    .then(() => console.log('数据库连接成功'))
    .catch(() => console.log('数据库连接失败'))
  2. 创建用户集合

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    // 创建用户集合
    // 引入mongoose第三方模块
    const mongoose = require('mongoose');
    // 导入bcrypt
    const bcrypt = require('bcrypt');
    // 引入joi模块
    const Joi = require('joi');
    // 创建用户集合规则
    const userSchema = new mongoose.Schema({
    username: {
    type: String,
    required: true,
    minlength: 2,
    maxlength: 20
    },
    email: {
    type: String,
    // 保证邮箱地址在插入数据库时不重复
    unique: true,
    required: true
    },
    password: {
    type: String,
    required: true
    },
    // admin 超级管理员
    // normal 普通用户
    role: {
    type: String,
    required: true
    },
    // 0 启用状态
    // 1 禁用状态
    state: {
    type: Number,
    default: 0
    }
    });

    // 创建集合
    const User = mongoose.model('User', userSchema);

    async function createUser () {
    const salt = await bcrypt.genSalt(10);
    const pass = await bcrypt.hash('123456', salt);
    const user = await User.create({
    username: 'iteheima',
    email: 'itheima@itcast.cn',
    password: pass,
    role: 'admin',
    state: 0
    });
    }

    // createUser();

    // 验证用户信息
    const validateUser = user => {
    // 定义对象的验证规则
    const schema = {
    username: Joi.string().min(2).max(12).required().error(new Error('用户名不符合验证规则')),
    email: Joi.string().email().required().error(new Error('邮箱格式不符合要求')),
    password: Joi.string().regex(/^[a-zA-Z0-9]{3,30}$/).required().error(new Error('密码格式不符合要求')),
    role: Joi.string().valid('normal', 'admin').required().error(new Error('角色值非法')),
    state: Joi.number().valid(0, 1).required().error(new Error('状态值非法'))
    };

    // 实施验证
    return Joi.validate(user, schema);
    }

    // 将用户集合做为模块成员进行导出
    module.exports = {
    User,
    validateUser
    }
  3. 完整的接口内容

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    // 导入用户集合构造函数
    const { User } = require('../../model/user');
    const bcrypt = require('bcrypt');

    module.exports = async (req, res) => {
    // 接收请求参数
    const {email, password} = req.body;
    // 如果用户没有输入邮件地址
    // if (email.trim().length == 0 || password.trim().length == 0) return res.status(400).send('<h4>邮件地址或者密码错误</h4>');
    if (email.trim().length == 0 || password.trim().length == 0) return res.status(400).render('admin/error', {msg: '邮件地址或者密码错误'});
    // 根据邮箱地址查询用户信息
    // 如果查询到了用户 user变量的值是对象类型 对象中存储的是用户信息
    // 如果没有查询到用户 user变量为空
    let user = await User.findOne({email});
    // 查询到了用户
    if (user) {
    // 将客户端传递过来的密码和用户信息中的密码进行比对
    // true 比对成功
    // false 对比失败
    let isValid = await bcrypt.compare(password, user.password);
    // 如果密码比对成功
    if ( isValid ) {
    // 登录成功
    // 将用户名存储在请求对象中
    req.session.username = user.username;
    // res.send('登录成功');
    req.app.locals.userInfo = user;
    // 重定向到用户列表页面
    res.redirect('/admin/user');
    } else {
    // 没有查询到用户
    res.status(400).render('admin/error', {msg: '邮箱地址或者密码错误'})
    }
    } else {
    // 没有查询到用户
    res.status(400).render('admin/error', {msg: '邮箱地址或者密码错误'})
    }
    }
  4. 入口文件

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    // 引用expess框架
    const express = require('express');
    // 处理路径
    const path = require('path');
    // 引入body-parser模块 用来处理post请求参数
    const bodyPaser = require('body-parser');
    // 导入express-session模块
    const session = require('express-session');
    // 创建网站服务器
    const app = express();
    // 数据库连接
    require('./model/connect');
    // 处理post请求参数
    app.use(bodyPaser.urlencoded({extended: false}));
    // 配置session
    app.use(session({
    secret: 'secret key',
    saveUninitialized: false,
    cookie: {
    maxAge: 24 * 60 * 60 * 1000
    }
    }));

    // 告诉express框架模板所在的位置
    app.set('views', path.join(__dirname, 'views'));
    // 告诉express框架模板的默认后缀是什么
    app.set('view engine', 'art');
    // 当渲染后缀为art的模板时 所使用的模板引擎是什么
    app.engine('art', require('express-art-template'));

    // 开放静态资源文件
    app.use(express.static(path.join(__dirname, 'public')))

    // 引入路由模块
    const home = require('./route/home');
    const admin = require('./route/admin');

    // 拦截请求 判断用户登录状态
    app.use('/admin', require('./middleware/loginGuard'));

    // 为路由匹配请求路径
    app.use('/home', home);
    app.use('/admin', admin);

    app.use((err, req, res, next) => {
    // 将字符串对象转换为对象类型
    // JSON.parse()
    const result = JSON.parse(err);
    res.redirect(`${result.path}?message=${result.message}`);
    })

    // 监听端口
    app.listen(80);
    console.log('网站服务器启动成功, 请访问localhost')