前端与移动开发----Node.js----express

Node.js

回顾

  1. 基础知识回顾

    node环境下的模块 - 类似于前端的js插件

    前端js插件: 挂载到window身上暴露出去

    node模块: 有CommonJS规范导出和导入的方法

    node模块, 每个外面运行时, 都会被套一个函数一共有5个本地参数

    • module - 模块内置对象
    • exports - 导出辅助对象
    • require - 引入其他模块的方法
    • __filename - 当前文件所在绝对路径
    • __dirname - 当前文件夹所在的绝对路径
  2. npm作用: node自带的, 可以用于管理第三方包(上传, 下载, 删除)

    包: 就是一个/一堆js模块组成的文件夹 - 一般包里得有一个package.json来管理这个包里的东西

    常用命令

    • npm init - 初始化项目 - 得到package.json
    • npm install 包名 - 安装一个包从npm源上下载相关代码到当前工程的node_modules目录下
    • npm install 工具包 -g 安装到全局(一般都是带命令的工具包)
    • nodemon 替代node命令来启动js文件 - 实时更新
    • i5ting_toc 用来把md文件转成网页
    • npm install 包名@版本号 (得去npmjs.cm网站上查)
  3. 开发自己的模块: 单独定义一个.js文件就能引入使用了

    开发自己的模块包:

    初始化package.json

    制定好main属性对应的入口文件(手动建立)

    编写相关的模块, 都引入 到入口文件 - 统一挂载暴露出去

  4. 上传包

    先去npmjs.com注册账号

    在本地切换会原来官网的镜像地址

    在本地npm login 建立登录通道

    npm publish 发布到网站上-注意所在目录应该是你开发的模块包下(必须的)

模块加载机制, 引入时, 向父级文件夹找node_modules下有无, 而且频繁引入同一个模块, node内部有缓存机制不会在让模块被重新执行了

10. express

express 介绍

  • express 是一个第三方模块,用于快速搭建服务器
  • express 是一个基于 Node.js 平台,快速、开放、极简的 web 开发框架
  • express基于http模块的基本API,使用express的时候,也能使用http的API
    • 使用express的时候,仍然可以使用http模块的方法,比如res.end()、req.url
  • express还额外封装了一些新方法,能让我们更方便的搭建服务器
    • web网页服务器
    • API接口服务器
  • express提供了中间件功能,其他很多强大的第三方模块都是基于express开发的
  • Express 官网
  • Express 中文文档(非官方)
  • Express GitHub仓库
  • 菜鸟教程
  • 腾讯云开发者手册

10.0 安装使用 express

本地生效, 执行 npm i express

不要自己定义文件夹名称和 - 已知的模块包名重名

使用Express构建Web服务器步骤

  1. 下载express模块包到项目工程中
  2. 新建服务器文件xxx.js
  3. 引入express模块
  4. 创建express服务器对象
  5. 配置端口, 开启服务器
// 使用express 搭建web服务器
// 1. 引入 express 模块
const express = require('express');

// 2. 创建 express 服务器
const app = express();

// 3. 开启服务器, 设置端口号(每个计算机, 端口号不能重复使用)
app.listen(3006, () => console.log('express服务器开始工作了'));

10.1 后台 - GET接口

// 监听客户端请求
// app.get('请求的URL', (请求对象, 响应对象) => {
//  当前台以get方式, 请求这个URL, 则此函数就执行
//});

app.get('/api/getbooks', (req, res) => {
    res.send("GET方式-请求了/api/getbooks"); // 结束响应, 返回内容(自带响应头UTF-8)
});

app.get('/', (req, res) => {
    // 客户端没有指定请求的url,在这里处理。
    res.send("GET方式-请求的根路径 /")
});

app.get('*', (req, res) => {
    res.send("GET方式-没有找到要请求的路径*")
})

app.get('*', (req, res) => {}) 他能够匹配到所有的GET请求,所以把它放到所有接口的最后。

10.2 后台 - 获取 - GET参数

  • 这种方式的参数,比较常规,参数部分也叫做查询字符串
  • 请求地址的写法: http://localhost:3002/test?id=3&bookname=zxx&age=20
  • 获取方式:req.query
// 写接口
// 前台请求: http://localhost:3002/test?id=3&bookname=zxx&age=20
app.get('/test', (req, res) => {
    // 获取url?参数=值&参数=值
    res.send(req.query); // { id: '3', bookname: 'zxx', age: '20' } - res.send还会内置转成JSON字符串, 再返回给前端
});

10.3 后台 - 获取 - url参数

  • 这种方式的参数,叫做动态参数
  • 请求地址的写法:http://localhost:3003/test/3/zs/30
  • 要求请求的url参数必填,否则不能和定义的接口匹配
// (1): 提前在路径上配置好key的名字, 注意:是必须的(规定)
// (2): express会自动在req的身上绑定req.params属性, 设置好路径对应位置的值和key形成对象

// 1个参数
// 浏览器的请求  http://localhost/test/3
// 测试接口,获取动态参数
app.get('/test/:id', (req, res) => {
    res.send(req.params); // req.query是查询字符串, req.params是路径上的参数{id: 3}
});

// 多个参数
// 浏览器的请求  http://localhost/test2/3/zhangsan/20
// 测试接口,获取多个动态参数
app.get('/test2/:id/:name/:age', (req, res) => {
    // 可以获取所有的动态参数  
    // { id: '4', name: 'zhangsan', age: '20' }
    res.send(req.params);
});

10.4 后台 - POST接口

// 监听客户端请求
// app.post('请求的URL', (请求对象, 响应对象) => {
//  当前台以post方式, 请求这个URL, 则此函数就执行
//});
app.post('/api/addbook', (req, res) => {
    res.send("POST方式, 请求/api/addbook");
});

app.post('*', (req, res) => {
    // 处理所有的POST请求
    res.send("post方式, 未找到你要请求的url");
})

10.5 后台 - 获取 - POST请求体 - 查询字符串

  • GET方式没有请求体,POST方式才有请求体。
  • 请求体,即客户端提交的数据。
  • POST请求体, 前端请求头使用Content-Type: application/x-www-form-urlencoded
// 客户端,需要发送POST请求,提交的数据随便写

// 写接口之前,配置
// 可以帮我们接收 content-type: application/x-www-form-urlencoded类型的请求体
app.use(express.urlencoded({extended: false}));

// 后续,任何一个POST接口,都可以通过req.body获取到请求体的内容
app.post('/api/addbook', (req, res) => {
    // 获取请求体
    console.log(req.body);
    res.send('你的请求体收到了');
});

10.6 后台 - 获取 - POST请求体 - JSON字符串

// 客户端,需要发送POST请求,提交的数据随便写

// 写接口之前,配置
// 可以帮我们接收 content-type: application/json
app.use(express.json());

// 后续,任何一个POST接口,都可以通过req.body获取到请求体的内容
app.post('/api/addbook', (req, res) => {
    // 获取请求体
    res.send(req.body);
});

10.7 后台 - 获取 - POST请求体 - FormData

  • POST请求头: FormData对象 – 对应的Content-Type: multipart/form-data; --XXADFsdfssf

需要使用第三方模块(multer)才能够获取到。

  • 下载安装第三方模块 multer
  • 使用步骤
    • 加载multer const multer = require('multer')
    • 配置上传目录 const upload = multer({ dest: 'uploads/' })
    • app.post(’/test’, upload.single('cover_img'), (req, res) => {})
    • 接口内部,可以使用req.body接收文本值,使用req.file接收文件信息
    • 同时,文件已经上传到了服务器
// 客户端,需要发送POST请求,提交的数据随便写
// 写接口之前,配置
const multer = require('multer');
// 配置上传目录
const upload = multer({ dest: 'uploads/' });

// 前台图片参数名: cover_img
app.post('/test', upload.single('cover_img'), (req, res) => {
    // 可以使用 req.body 接收文本信息
    console.log(req.body); // { username: 'laotang', nickname: 'boy' }
    console.log('=====================');
    // 可以使用 req.file 获取文件信息
    console.log(req.file);
    /**
     *  {
            fieldname: 'cover_img',
            originalname: '1.jpg',
            encoding: '7bit',
            mimetype: 'image/jpeg',
            destination: 'uploads/',  // 上传的目录
            filename: '346d149a0a79c5c9792367c49d3db5ae', // 上传之后新文件的名字
            path: 'uploads/346d149a0a79c5c9792367c49d3db5ae',
            size: 35774
        }
     */
    res.send({
        ...req.body,
        fileUrl: req.file.filename
    });
});

小结

  • 搭建服务器的步骤
    • const express = require(‘express’);
    • const app = express();
    • app.listen(3006, () => console.log(’…’));
    • 配置及写接口
  • GET方式如何获取请求参数
    • 查询字符串格式
      • req.query
    • url参数(动态参数)
      • req.params
  • POST方式如何获取请求体
    • 查询字符串格式
      • 配置:app.use(express.urlencoded({ extended: false }))
      • req.body
    • FormData格式
      • 配置:
        • 下载安装 multer
        • const multer = require(‘multer’);
        • const upload = multer({ dest: ‘uploads’ });
        • app.post(‘接口地址’, upload.single(‘文件的key’), ‘处理函数’);
      • req.body – 获取文本信息
      • req.file – 获取文件信息
      • 同时,文件已经上传完毕

10.8 multer高级用法 - 改文件名

// 1. 引入multer, 下载并引入uuid模块(用于生成一个唯一不重复的字符串)
const multer = require('multer');
const uuidv4 = require("uuid").v4;

// 2. multer - diskStorage 磁盘存储控制
const storage = multer.diskStorage({
    destination: "uploads/", // 文件存到哪里
    filename: function (req, file, cb) { // 设置文件名
        const parts = file.originalname.split('.') // 用.分割
        const ext = parts[parts.length - 1] // 提取最后一个是扩展名
        cb(null, `${Date.now()}_${uuidv4().replace(/-/g, '')}.${ext}`) // 时间戳_随机字符串.扩展名格式作为文件的filename名字
    }
})

// 3. 生成multer上传工具
const upload = multer({ storage });


// 4. single方法在原地返回一个函数体, 接受名称为的单个文件fieldname。单个文件将存储在中req.file。
// app.post()接收到请求, 先被第二个函数执行, 再被第三个函数执行, 直到遇到res.send结束本次请求
app.post('/test', upload.single('cover_img'), (req, res) => {
    res.send({
        ...req.body,
        fileUrl: "http://localhost:3008/" + req.file.filename
    });
});

// 5. 设置静态资源路径, 当前端请求url在上面未命中, 就会执行这里. 尝试在uploads下面匹配资源
app.use(express.static("uploads")); 

11. express更多用法

11.0 静态资源配置

服务器提供的资源分为2种

  • 文件等资源
  • 接口数据资源

浏览器想直接访问后台服务器上的资源, 填写路径就能访问, 需要后端先配置静态资源目录

// 配置静态资源目录
// (1): 这个文件夹不会出现在前端的路径上
// (2): 前端可以直接访问这个路径下的一切
app.use(express.static("静态资源")); // app.use()让app对象使用这个功能

// 如果要是还有一个文件夹, 接着写即可, 从上到下的顺序匹配
app.use(express.static("uploads"));

11.1 前缀虚拟路径

让别人猜不出你文件夹真实的名字, 随便写一个虚拟的前缀路径来使用

app.use("/abc", express.static("静态资源"));
app.use(express.static("uploads"));

11.2 express路由

前端与移动开发----Node.js----express_第1张图片

  • 路由:即请求和处理程序的映射关系

12. 案例 - 图书管理案例

现在还没有学数据, 所以在后台的数组里先保存下(只要后台不重启后台的js文件, 那么数组里的值一直再, 如果重新数组就回归初始化)

12.0 搭建web服务器

  • 配置静态资源 - 既要提供图书管理案例的网页 - so不要再vscode启动(不用vscode的插件live server内置的微型服务器) - 直接打开浏览器访问
let arr = [];

// 静态资源网页
app.use("/html", express.static(__dirname + "/图书管理系统"));
// 解析application/x-www-form-urlencoded
app.use(express.urlencoded({extended: false}));
// 解析application/json
app.use(express.json());

12.1 新增功能前后台使用

// resetFul API规范的接口
// 评论管理 - restFul API
// RESTFUL是一种网络应用程序的设计风格和开发方式接口的场景,动作类型为查询, 新增、变更、删除所调用资源。
// 1、每一个URI代表1种资源, 路径带参
// 2、客户端使用GET、POST、PUT、DELETE4个表示操作方式的动词对服务端资源进行操作
app.post("/api/book", (req, res) => {
    req.body.id = arr.length + 1;
    arr.push(req.body);
    res.status(201).send({
        msg: "新增成功",
        status: 0,
        data: req.body
    })
})

前台:

// 1.1 写一段代码,请求接口,添加图书
$('#btnAdd').click(function () {
    // 1.2 获取输入框的值(三个)
    var bookname = $('#iptBookname').val().trim();
    var author = $('#iptAuthor').val().trim();
    var publisher = $('#iptPublisher').val().trim();
    // 1.3 判断不能为空,如果有一个为空,提示并终止代码继续执行
    if (bookname == '' || author == '' || publisher == '') {
        return alert('内容不能为空');
    }
    // 1.4 按照接口文档,调用添加书籍的接口,完成添加
    axios({
        method: "POST",
        url: "http://localhost:3000/api/book",
        data: {
            bookname: bookname,
            author: author,
            publisher: publisher
        }
    }).then(res => {
        // 添加成功
        var obj = res.data.data; // 接受后台返回插入成功的这个对象(带后台生成的id的)
        console.log(obj);
        $("#tb").prepend($(`
${obj.id}
${obj.bookname}
${obj.author}
${obj.publisher}

${obj.id}">删除

`))
    })
});

12.2 查询功能前后台实现

后台:

app.get("/api/book", (req, res) => {
    res.send({
        msg: "获取成功",
        status: 0,
        data: arr
    })
});

前台:

// 写一段代码,请求接口,获取所有的图书
        function getBooks() {
            $("#tb").empty();
            // 把数据请求回来
            axios({
                url: "http://localhost:3000/api/book",
                method: "GET"
            }).then(res => {
                // 提取数据 - 铺设页面
                $(res.data.data).each(function (index, obj) {
                    $("#tb").prepend($(`
                                ${obj.id}
                                ${obj.bookname}
                                ${obj.author}
                                ${obj.publisher}
                                
                                ${obj.id}">删除
                                
                            `))
                })
            })
        }

        // 1.5 网页打开 - 到这里直接调用
        getBooks();

12.3 删除功能前后台实现

后台:

app.delete("/api/book/:id", (req, res) => {
    // 找到id在数组里对象的索引
    let ind = arr.findIndex(obj => obj.id == req.params.id);
    arr.splice(ind, 1);
    res.send({
        msg: "获取成功",
        status: 0,
        data: arr
    })
})

前台:

// // 3.1 因为a是动态创建的, 所以在这里要执行 - 只能用事件委托, 不然得写在getBoods里面的下面 再绑定, 代码写在一起不好
$('#tb').on('click', '.del', function () {
    // 3.2 对于敏感操作,最好给一个提示(询问是否要删除)
    if (!confirm('你确定要删除吗,你好狠!')) {
        return;
    }
    // 3.3 如果确定是删除,按照接口,完成接口的要求
    var id = $(this).attr('data-id');
    axios({
        method: "DELETE",
        url: "http://localhost:3000/api/book/" + id
    }).then(res => {
        getBooks();
    })
});

如有不足,请多指教,
未完待续,持续更新!
大家一起进步!

你可能感兴趣的:(node.js,前端与移动开发学习笔记,后端,node.js,前端)