node.js简单实用的一门后台语言

400 阅读19分钟

node是什么

Node.js是JS的后端运行环境

Node.js 是一个基于 Chrome V8 引擎 的 JavaScript 运行时环境

image.png

打开方式

  • 直接win+r的方式打开cmd的方式打开小黑窗
  • 在任意目录的最上方路径输入框中输入 cmd 回车接口打开 (推荐)
  • 在任意目录中按住 shift键 + 鼠标右键后在弹出菜单中点击 在此处打开命令窗口 或 在此处打开PowerShell窗口即可打开
  • 在VSCode中,任意一个js文件上鼠标右键,选择在 集成终端中打开

在node环境中运行js代码

打开小黑窗,输入 node 执行体验.js 回车即可

node后面需要空格 可以直接tab自动填充

终止当前node可以按ctrl+c

复制粘贴都是用鼠标右键

常用操作命令

清空输入 clear或者cls

查看列表 ls/list/dir

进入某个目录 cd 目录名

返回上一个目录 cd ..

回到根目录 cd \

切换历史输入 ⬆ ⬇

文件读取模块

核心模块就是nodejs自带的模块,在安装完nodejs之后,就可以任意使用啦。相当于学习js时使用的Math对象一样

查询官网

nodeapi官网:nodejs.org/dist/latest…

语法

const 变量名(自己随意取) = require('核心模块名');

文件读写模块

const fs = require('fs')

异步读取文件

fs.readFile("./data/01.txt", function(err,data){
    // data返回buffer对象(二进制对象),可以通过toString方法转换为正常文字
    console.log(err,data.toString());
})

同步读取文件

// try {
    // 这里写正常的逻辑代码
// } catch (error) {
    // 如何try中的代码报错了,那么这个错误就会被catch捕捉到
    // 这个错误可以通过catch的参数获取到
    // 所以,这里就是用来做一些错误处理地方
// }

try {
    const  res = fs.readFileSync("./666.txt")
    console.log(4,res);
} catch (error) {
    console.log(19,"发生错误了",error);
}

异步写入内容

// 写入成功案例,因为writeFile方法会自动帮我们创建03.txt文件
fs.writeFile("./data/03.txt","等闲变却故人心,故人却道心易变",(err)=>{
    if(err){
        console.log("发生错误:",err);
        return
    }
    console.log("写入成功");
})

同步写入内容

try {
    fs.writeFileSync("./data/04.txt","君不见高堂明镜悲白发,朝如青丝暮成雪;")
    console.log("写入成功");
} catch (error) {
    console.log("写入失败:",error);
}  //同步与异步写入内容都会将之前已有的内容覆盖

异步追加内容

// \n: 表示换行符
fs.appendFile("./data/03.txt","\n何如薄幸锦衣郎,比翼连枝当日愿!",(err)=>{
    if(err){
        console.log("写入失败:",err);
        return
    }
    console.log("写入成功!");
})

同步追加内容

try {
    fs.appendFileSync("./data/04.txt",",奔流到海不复回;")
    console.log("写入成功");
} catch (error) {
    console.log("写入失败:",error)
}

Path模块

作用:用来处理路径的拼接,分析,取后缀名

常用的方法

image.png

__dirname: 获取js文件在node中执行时的绝对路径

Http模块

浏览器 请求 资源 要遵守 http 协议: 请求报文(请求行,请求头,请求体)

服务器 返回 资源 要遵守 http 协议: 响应报文(响应行,响应头,响应体)

一般用来创建web服务器

基本用法

const http = require("http");

const app = http.createServer(function(req,res){
    // req(request): 请求报文
    // res(response): 响应报文
    // 每次浏览器访问服务器的时候,都会触发这个回调函数
    console.log("服务器已经在运行了");
    // res.end方法用来返回数据给浏览器
    res.end("ok")
})

app.listen("8001",()=>{
    // listen是用来监听端口号,这里的回调函数只会触发一次
    console.log("web服务器启动成功:http://127.0.0.1:8001");
})

req.url

通过 req.url来获取当前请求的url路径

const http = require("http");

const app = http.createServer(function(req,res){
    // req:也就是回调函数的第一个参数,一定是请求报文
    // res:也就是回调函数的第二个参数,一定是响应报文
    //如何知道浏览器访问的路径?
    // 答:通过 req.url来获取浏览器访问的路径(不包含基础路径)
    console.log(req.url);
    // setHeader: 设置响应头,content-type用于设置返回给前端的数据类型
    // 数据类型我们如何知道呢?答:通过一个网站查询:https://developer.mozilla.org/zh-CN/docs/Web/HTTP/Basics_of_HTTP/MIME_types
    res.setHeader("content-type","text/html;charset=utf-8")
    // 实现获取用户信息接口
    if(req.url === "/admin/user/info"){
        let obj = {
            nickname: "小明",
            userPic: "http://127.0.0.1:8000/123.png"
        }
        // end方法中只接收字符串或者buffer对象
        res.end(JSON.stringify(obj))
    }else{
        // 设置请求状态码
        res.statusCode = 404;
        // 注意:返回参数中的404是假的404,只是后端人员为了告诉前端人员错误的原因
        // res.end表示请求结束了,并返回数据给前端
        res.end("{msg: '请求失败',code: 404}")
    }
})

// 端口号自己声明,值的访问0-6万多
// 建议大家,端口号取值4位数
app.listen("8000",()=>{
    console.log("服务器启动成功:http://127.0.0.1:8000");
})

res.setHeader()设置响应头解决中文乱码

常见的几种文件类型及content-type

image.png

查询网址

res.end()设置响应体

语法:res.end(响应的数据)end()只能传入buffer或者是String类型的数据

res.statusCode设置状态码

res.statusCode = 状态码,状态码是http协议规定的 状态码: 500 (服务器异常) 404(资源找不到)

批量处理.html中的二次请求

const fs  = require("fs");
const http = require("http");
const path = require("path");

let extType = {
    ".html": "text/html;charset=utf8",
    ".css": "text/css;charset=utf8",
    ".js": "application/javascript",
    ".jpg": "image/jpg"
}
const app = http.createServer((req,res)=>{
    // res.end("ok")
    // 根据访问文件的后缀名可以动态设置content-type的值
    // 如何获取到文件的后缀名呢?
    // 答:通过path.extname方法获取文件后缀名

    let _url = "./public"+req.url;
    fs.readFile(_url,(err,data)=>{
        if(err){
            console.log("读取失败:",err);
            res.setHeader("content-type","text/html;charset=utf8" )
            res.end("读取失败!")
            return
        }
        res.setHeader("content-type",extType[path.extname(req.url)] )
        res.end(data);
    })
})

app.listen("8003",()=>{
    console.log("服务器开启成功:http://127.0.0.1:8003");
})

web服务器通过req.method获取当前请求的类型

/**
 * 静态资源:返回给前端的是一个个文件
 * 接口:返回给前端的是数据
 * 
 * nodejs如何启动一个接口
 * 
 * 请求方法:
 *  常用的:get,post
 * */ 

 const http = require("http");

 const app = http.createServer((req,res)=>{
     // 如何获取到请求方式:
     // 答:可以通过req.method属性获取到
     // 需求:实现get请求,请求地址是/api/getBookList,请求参数是bookname,搜索书籍接口

    //  获取查询参数
    // req.url包含了查询参数
    console.log(req.url);
    let arrUrl = req.url.split("?");
    console.log(22,arrUrl);
     if(req.method == "GET"&&arrUrl[0] === "/api/getBookList"){
         // 请求地址和方式正确,返回数据给前端
         let arr = [
             {
                 name: "红楼梦",
                 author: "曹雪芹"
             },{
                 name: "三国演义",
                 author: "罗贯中"
             }
         ]
        //  arrUrl[1] 的值就是 bookname=三国演义

        //  获取查询参数
        // URLSearchParams:是一个构造函数,用来将查询字符串转换为对象

        // 然后我们就能够通过URLSearchParams的实例化对象的get方法获取到查询的参数的值
        // 实例化对象:指的就是new构造函数的返回值,这个例子中指的就是query这个对象
        let query = new URLSearchParams(arrUrl[1]);
        console.log(42,query) // { 'bookname' => '三国演义' } 这个就相当于前端中的对象的形式{bookname: '三国演义'}
        let str = query.get("bookname"); // bookname表示获取对象中键值对应的键名
        
        // filter方法是js中数组的过滤器方法
        let newArr = arr.filter(item=>{
            return item.name == str;
        })
        // console.log(41,newArr);
         res.setHeader("content-type","application/json;charset=utf8")
         res.end( JSON.stringify(newArr) )
     }else{
         res.statusCode = 404;
         res.setHeader("content-type","text/html;charset=utf8");
         res.end("{msg: '访问地址获取方式错误',code: 404}")
     }
     
 })
 
 app.listen("3000",()=>{
     console.log("服务器启动成功:http://localhost:3000");
 })
 

URLSearchParams 解析参数

参考文档

Post接口

利用postman模拟数据post

let result ="";
    req.on("data", (chunk)=>{
        // chunk: 就是接收到的前端发送过来的数据片段(或者是完整的)
        console.log(123,chunk);
        result+=chunk;
    })
    req.on("end",()=>{
        console.log("数据接收完成了:", result);
    })
    res.end("post接口")

post请求监听data

data事件,每次收到一部分参数数据就会触发一次这个事件。

post请求监听end

end事件,全部的参数数据接收完成之后会执行一次。

node模块化

每个js文件看作是一个模块,并且通过固定的方式向外暴露指定的内容,每个模块通过固定的方式引入

作用

  • 能够对一类功能做很好的分类管理
  • 能够保护成员不被污染
  • 不用考虑导入顺序按需导入
  • 可以随时更换模块,维护方便

写法

// 封装加法运算
function add(a,b){
    return a+b;
}

module.exports = {
    add
}

导出模块化

exports是module.exports的别名

function add(a,b){
    return a+ b;
}
// 方式一
 exports.add = add;
// 方式二
 module.exports = {
//     add
// }
// 方法三
module.exports.add = add;

引入自定义模块

const test = require("定义模块的路径.js")

npm管理工具

node的包管理工具地址

npm安装和使用包

  1. 执行npm init --yes 初始化package.json
  2. 去npmjs.com网站 搜索 关键字 找到合适的包
  3. 按照文档完成下载(使用npm install 包名称 下载)
  4. 通过require引入包

全局安装

命令:npm install -g 包名 或者 npm install 包名 -g

查看全局的node_modules路径

npm root -g // 查看全局包的安装目录 
npm list -g --depth 0 //查看全局安装过的包

nodemon依赖包的使用

它能替代node命令去帮我们执行一个js文件,并自动检测到我们的代码如果有修改就会自动重新运行我们的代码

 * 作用:nodemon替换node命令,实时监听代码的变化并执行。
 * 使用步骤:
 * 1、全局下载 npm i -g nodemon (注:i是install的简写)
 * 2、可以在任意目录下使用nodemon命令了
 * 3、语法:nodemon 要执行的js代码的路径

忽略监控data.json文件,nodemon --ignore data.json server.js

nrm切换安装包的下载源地址

  • 第一步: 全局安装 npm install nrm -g
  • 第二步:列出所有的源信息 (*)标注的就是当前使用的源 使用 nrm ls 命令可以查看
  • 第三步:根据需要切换源 使用 nrm use taobao 切换到taobao源

npm卸载

本地包卸载

  1. 进入到你想要卸载的包所在的文件夹,到package.json这一层即可
  2. 打开cmd小黑窗
  3. 在小黑窗中执行 npm uninstall 包名 简写: npm un 包名 npm un 包名1 包名2

全局包卸载

  1. 在任意地方打开小黑窗
  2. 输入 npm uninstall 全局包名 -g 简写:npm un 全局包名 -g npm un 包名1 包名2 -g

开发依赖和生产依赖

--save-dev:表示安装的包是一个开发依赖包,保存到开发依赖(package.json的devDependencies)

--save:表示安装的包是一个生产依赖包,保存到生产依赖(package.json的Dependencies)

用npm发布包

请先去npmjs.com上注册一个用户,并通过邮箱验证

项目初始化: 创建文件夹,小黑窗中输入:npm init --yes

通过 npm view 文件名 检查一下npmjs上是否有同名的包

切换当前npm源到npmjs上: nrm use npm

连接npm: npm adduser (输入npmjs.com上注册时的用户名,密码,邮箱)

把包上传到npm: npm publish

后续更新需要更改版本号才可以publish

npm unpublish 包名称 --force //强制删除

require加载包

加载核心模块,直接从内存中加载,并缓存,同一个模块第一次require之后,就会缓存一份,下一次require时就直接从缓存中去取。

require加载自定义模块,没有扩展名的情况下,查找文件顺序

// 当我们使用require引入模块之后,该模块会别缓存起来,下一次引入的该模块,就是从缓存中获取的
const fs = require("fs");
const fs1 = require("fs");

console.log(fs1 === fs );

// 引入自定义模块
// 引入模块的时候可以省略文件的后缀名
// 省略后缀名后,require是如何查找文件的呢?
// 答:通过如下顺序查找
        // 1、找tool文件夹
        // 2、找不到,再找tool.js文件
        // 3、找不到,再找tool.json文件
        // 4、找不到,再从内置模块中找
        // 5、找不到,最后就报错啦 
        // node_modules
// 引入文件的时候建议加上后缀名,这样可以省略查找的过程(加载速度更快)
const tool = require("./02模块化/tool")

加载第三方模块的格式是 const xxx = require("模块名") ,如:require("dateformat") 并缓存

// node 会去本级目录 node_modules下查找dateformat模块
// 如果找不到,则查找上级目录node_modules下查找dateformat模块
// 直到查找到盘符根目录下
// 如果每一级目录都找不到就报模块找不到错误,如果在某一级目录找到了,则停止查找

express框架

中文文档

npm安装express框架

创建web服务器

// 1、引入框架
const express = require("express");

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

// 3、设置回调,响应数据给前端
// 这个斜杠是浏览器访问“127.0.0.1:8080”时的默认路径
// express提供了专门用来定义get接口和post接口方法的
// 分别是:app.get("接口路径", 回调函数)
//         app.post("接口路径",回调函数)
app.get("/",(req,res)=>{
    // 通过send方法设置响应数据,这个方法会自动根据内容决定响应content-type的值。
    res.send("你好,express---666");
})

// 4、监听端口
app.listen("8080")

express使用send()响应内容

常用方法

req.url  // 获取请求url
req.method  // 获取请求方法
req.query  // 获取url传入的查询字符串参数 返回查询参数的对象
req.params  // 获取url传入的路由参数
req.body  // 获取post请求体中的参数 需要配合下面内置中间件完成 
app.use(express.urlencoded()) // x-www-form-urlencoded对象  app.use(express.json())  // json对象
res.send() // 作用类似于http模块中的res.end() 举例:res.send('要响应的数据') 
//它会自动增加content-type响应头,支持直接js对象参数作为响应数据 res.send({name:"张三"})
res.json()  // 直接将一个js对象或者数据以json字符串返回:res.json({name:"张三"})
res.status()  //设置响应状态码: res.status(404)
res.set()  // 设置响应头
    res.set('content-type','text/html;charset=utf8') 
    res.set('Access-Control-Allow-Origin',  //设置跨域访问

express静态托管资源

const express = require("express");

const app = express();

// 静态文件托管,直接使用express.static方法托管即可,可以托管多份项目
//一般静态资源html页面文件需设置成index.html
// 如何托管多份项目呢?
// 答:app.use方法可以传2参数,第一个参数就是自定义静态资源访问的路径,
// 第二个参数就是express.static用来设置托管项目文件名

app.use("/static",express.static("public"))
app.use("/test",express.static("测试"))

app.listen("3000",()=>{
    console.log("服务器启动成:127.0.0.1:3000");
})

文件上传multer

文档

/***
 * 需求:实现文件上传的功能
 * 1、利用multer中间件来上传,下载multer(mpm i multer)
 * 2、引入multer
 * 3、利用multer配置上传文件的存储位置
 * 4、在定义post接口的时候,要在接口名称和回调函数之间添加一个参数(multer.single('cover')),设置接收文件的字段名
 * 
 * */ 

const express = require("express");
const multer = require("multer");
// 通过multer配置上传文件的存储位置
const upload = multer({ dest: "uploads/" }) // uploads这个名字可以自己定义

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

// 前端传值:{cover: 文件对象}
// upload.single定义文件上传时的字段名(属性名),这个cover也是后端人员自定义的
app.post("/upload",upload.single("cover"),function(req,res,next){
    res.set("Access-Control-Allow-Origin", "*")
    res.send({
        status: 200,
        msg: "上传成功!"
    })
})


app.listen("8080",()=>{
    console.log("服务器启动成功:http://127.0.0.1:8080");
})

修改上传文件的文件名

const express = require("express");
const multer = require("multer");
// 通过multer配置上传文件的存储位置

let storage = multer.diskStorage({
    // 配置文件存储路径
    destination: function (req, file, cb) {
        cb(null, "tmp/my-uploads")
    },
    // 配置自定义文件名
    filename: function (req, file, cb) {
        cb(null, file.fieldname + "-" + Date.now())
    }
})
const upload = multer({ storage: storage })

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

// 前端传值:{cover: 文件对象}
app.post("/upload", upload.single("cover"), function (req, res, next) {
    res.set("Access-Control-Allow-Origin", "*")
    res.send({
        status: 200,
        msg: "上传成功!"
    })
})
app.listen("8080", () => {
    console.log("服务器启动成功:http://127.0.0.1:8080");
})

路由中间件

使用场景:接口数量较多时,需要分类别管理,此时就需要用到路由中间件

首页代码

const express = require("express");

const app = express();

// 5、引入user路由
const userRouter = require("./user/userRouter");
// 6、使用user路由
app.use("/admin/user",userRouter)

app.listen("8080",()=>{
    console.log("服务器启动成功:http://127.0.0.1:8080");
})

路由页代码

// 用户相关接口(通过路由中间件实现)
// 1、引入express
const express = require("express");
// 2、通过express生成路由对象
const Router = express.Router();

// 3、定义路由接口路径,并定义处理函数
// 登录接口
Router.post("/login",(req,res)=>{
    res.send({
        status: 200,
        msg: "登录成功!"
    })
})
// 获取用户信息接口
Router.get("/info",(req,res)=>{
    res.send({
        status: 200,
        msg: {
            username: "小明",
            age: 18
        }
    })
})
// 4、导出router模块
module.exports = Router;

路由中间件作用

express中间件是一个特殊的url地址处理函数,一个 express 应用,就是由许许多多的中间件来完成的

  • 执行任何代码。
  • 修改请求和响应对象。
  • 终结请求-响应循环(结束请求)。
  • 调用堆栈中的下一个中间件

中间件本质就是一个函数,它被当作 app.use(中间件函数) 的参数来使用

app.use((req,res,next)=>{
    let token = true;
    // 当我们的判断成功的时候,需要做下一步操作,不成功则不需要做下一步操作,那么成功的时候如何进行下一步呢?
    // 答:中间提供了next函数,那么我们只需要调用这个next函数即可
    if(token){
        // 当token值存在的时候表示用户已经登录
        next() //只有显示调用了next()才会进入到下一个中间件或者路由处理函数的执行,
               //否则就到此为止
    }else{
        res.send({
            msg: "请先登录!",
            code: 500
        })
    }
})

中间件分类

  • 应用级中间件
  • 内置中间件
  • 路由级中间件
  • 错误处理中间件
  • 第三方中间件

错误中间件

// 项目中有很多接口,都需要处理错误信息
// 这个错误中间件所有请求都会经过他,因为可以统一封装错误信息
// 错误中间件的作用(所有的接口都会经过错误中间件):用来统一设置返回给前端的错误信息
app.use((err,req,res,next)=>{
        res.send({
            msg: err.path,
            code: 500
        })
})

跨域中间件

解决浏览器跨域请求res.set("Access-Control-Allow-Origin", "*")

const cors = require("cors")
app.use(cors())

验证token中间件

const jwt = require("express-jwt")

// token验证中间件
// unless排除不需要验证的路由(接口)
app.use(jwt({
    secret: 'heima71', algorithms: ['HS256']
}).unless({ path: ['/user/register', '/user/login'] }));

app.use(function (err, req, res, next) {
    // 当token验证失败的时候,err.name的值就等于UnauthorizedError
    if (err.name === 'UnauthorizedError') {
        res.status(401).send('invalid token...');
    }
});

业务代码1

// 用户相关接口

const express = require("express");
const sqlQuery = require("../utils/db");

const Router = express.Router();

// 写业务
// 注册路由(接口)
/**
 * 实现注册路由的功能分析
 * 1、接收前端传递过来的用户名密码
 * 2、判断用户名密码是否为空(不允许空)
 * 3、判断用户名是否已存在
 * 4、新增用户
 * 
 * */
Router.post("/register", (req, res) => {
    // 1、获取用户名和密码
    let { userName, userPwd } = req.body;
    // console.log(userName, userPwd);
    // 2、判断用户名和密码是否为空
    if (userName && userPwd) {
        // 3、判断用户名是否已经被注册了
        sqlQuery(`select * from user where username="${userName}"`, (err, result) => {
            console.log(err);
            if (err) {
                return res.send({
                    code: 500,
                    msg: err.message
                })
            }
            // console.log(33, result); // result得到的结果是一个数组,当数组内有数据的时候表示该用户名已经被注册了
            // 4、新增用户
            if (result.length === 0) {
                // 没有被注册

                let sql = `insert into user(username,password) values("${userName}","${userPwd}")`;
                sqlQuery(sql, (err, result) => {
                    if (err) {
                        return res.send({
                            code: 500,
                            msg: err.message
                        })
                    }
                    res.send({
                        code: 200,
                        msg: "用户注册成功!"
                    })
                })
                return
            }
            // 该用户名被注册了
            res.send({
                msg: "该用户名被注册了,请求更改!",
                code: 201
            })
        })
    } else {
        res.send({
            code: 201,
            msg: "用户名和密码不能为空,请检查!"
        })
    }
})

// 登录功能
/**
 * 1、写好接口的路由
 * 2、获取前端传递过来的用户名和密码
 * 3、根据用户名和密码查询数据库数据,看是否存在这样的用户
 * 4、如果查询有这个用户:code / msg / token
 *      考虑token如何生成?要用的到一个插件生成token,jsonwebtoken
 * 
 * */
Router.post("/login", (req, res) => {
    // 2、获取用户名密码
    let { userName, userPwd } = req.body;
    // 3、根据用户名密码查询数据库中是否存在改用户,
    // 如果查询结果返回一个数组,且数组长度大于0说明该用户存,则返回登录成功
    let sql = `select * from user where username="${userName}" and password="${userPwd}"`;
    sqlQuery(sql, (err, result) => {
        if (err) {
            return res.send({
                msg: err.message,
                code: 500
            })
        }
        if (result.length > 0) {
            return res.send({
                code: 200,
                msg: "登录成功!",
                token: createToken(userName)
            })
        }
        res.send({
            code: 201,
            msg: "账号或密码错误,请检查!"
        })
    })
    // res.send("ok")
})

// 封装创建token的函数
var jwt = require('jsonwebtoken');
function createToken(username) {
    // 设置加密密码
    // sign函数中3个参数的说明
    /**
     * 第一个参数:生成token的数据(我们对用户名进行加密即可)
     * 第二个参数:加密的密码(以后解析前端传递过来的token时需要用到)
     * 第三个参数:设置token的过期时间
     * */
    let password = "heima71";
    let token = jwt.sign({
        username: username
    }, password, { expiresIn: 60 * 60 * 24 });
    // token前面必须添加"Bearer "字符串,这个字符串用在验证token需要用到的;注意:Bearer后面必须有一个空格在,
    return "Bearer " + token;
}

// 导出
module.exports = Router;

业务代码2

// 英雄相关接口
const express = require("express");
const sqlQuery = require("../utils/db");
const path = require("path")

const Router = express.Router();

// 写业务
// 获取英雄列表接口
/**
 * 1、获取前端传入的英雄名
 * 2、构建sql语句查询数据
 * (判断用户是否传递了英雄名,如果没有传,则读取所有英雄数据返回给前端,如果有传,需要根据英雄名查询数据返回给前端)
 * 3、利用mysql查询数据并返回即可
 * 
 * */
Router.get("/getHeroList", (req, res) => {
    // 获取英雄名字
    let { heroName } = req.query;
    // 构建sql语句
    let sql = `select * from heros`;
    if (heroName) {
        sql += ` where name="${heroName}"`
    }
    // 利用mysql查询数据并返回即可
    sqlQuery(sql, (err, result) => {
        if (err) {
            return res.send({
                msg: err.message,
                code: 500
            })
        }
        res.send({
            code: 200,
            msg: "获取成功!",
            data: result
        })
    })
})

// 上传头像
/**
 * 1、使用multer中间件上传图片,根据文档上传的步骤
 * 
 * 2、上传成功后返回数据给前端
 * */
const multer = require('multer')
// const upload = multer({ dest: 'uploads/' })
const storage = multer.diskStorage({
    destination: function (req, file, cb) {
        cb(null, 'uploads/')
    },
    filename: function (req, file, cb) {
        //  path.extname(file.originalname)获取文件后缀名
        // console.log(55, file);
        const filename = Date.now() + '-' + Math.round(Math.random() * 1E9) + path.extname(file.originalname)
        cb(null, filename)
    }
})

const upload = multer({ storage: storage })

Router.post("/uploadFile", upload.single('file_data'), (req, res) => {
    // console.log(req.file);
    res.send({
        code: 200,
        msg: "头像上传成功",
        src: "/" + req.file.path
    })
})

// 更新英雄数据
/**
 * 1、获取前端传递过来的数据(id必传)
 * 2、判断id是否存在
 * 3、构建sql语句
 * 4、根据构建sql语句更新数据即可(mysql模块)
 * 
 * */
Router.post("/updateHero", (req, res) => {
    // 1、获取数据
    let { id, name, gender, img } = req.body;
    // 2、判断id是否存在
    if (id) {
        // 更新
        // 3、构建sql语句
        let sql = `update heros set `;
        let arr = [];
        if (name) {
            arr.push(`name="${name}"`)
        }
        if (gender) {
            arr.push(`gender="${gender}"`)
        }
        if (img) {
            arr.push(`img="${img}"`)
        }
        sql += arr.join(",") + ` where id="${id}"`;
        // 4、实现更新
        sqlQuery(sql, (err, result) => {
            if (err) {
                return res.send({
                    msg: err.message,
                    code: 500
                })
            }
            res.send({
                msg: "更新成功!",
                code: 200
            })
        })

    } else {
        res.send({
            code: 201,
            msg: "id为必传值,请检查!"
        })
    }
})

// 添加英雄数据
/**
 * 实现添加英雄分析
 * 1、获取前端传递过来的参数
 * 2、判断英雄名是否存在
 * 3、构建sql语句
 * 4、根据sql语句向数据库插入数据完成添加功能
 * 
 * */
Router.post("/addHero", (req, res) => {
    let { name, gender, img } = req.body;
    if (name) {
        // 名字必须存在
        let sql = ` insert into heros(name,gender,img) values("${name}","${gender}","${img}") `;
        sqlQuery(sql, (err, result) => {
            if (err) {
                return res.send({
                    msg: err.message,
                    code: 500
                })
            }
            res.send({
                msg: "新增成功!",
                code: 200
            })
        })
    } else {
        res.send({
            msg: "名字不能为空",
            code: 201
        })
    }
})

// 删除接口 需要配置路由参数
/**
 * 实现步骤
 * 1、获取前端传递过来的英雄id
 * 2、判断id是否存
 * 3、构建sql语句,
 * 4、进行删除操作
 * 
 * */
Router.get("/delHeroById/:id", (req, res) => {
    let { id } = req.params;
    if (id) {
        // id存在,进行删除操作
        let sql = ` delete from heros where id="${id}" `;
        sqlQuery(sql, (err, result) => {
            if (err) {
                res.send({
                    code: 500,
                    msg: err.message
                })
                return
            }
            res.send({
                msg: "删除成功",
                code: 200
            })
        })
    } else {
        res.send({
            msg: "id不能为空!",
            code: 201
        })
    }
})

// 导出
module.exports = Router;

RESTful接口

  • RESTful API是目前比较成熟的一套互联网应用程序的API设计理论
  • REST本身并没有创造新的技术、组件或服务,REST指的是一组架构约束条件和原则

image.png

MySQL

数据库 (database) 是用来组织、存储和管理数据的一个仓库。

开启数据库

    1. phpstudy集成软件管理mysql (小皮面板(phpstudy) - 让天下没有难配的服务器环境! (xp.cn))
    1. Navicat是一个管理MySQL数据的工具 Navicat | 支持 MySQL、MariaDB、MongoDB、SQL Server、SQLite、Oracle 和 PostgreSQL 的数据库管理

sql语句

-- 插入数据
INSERT INTO skin(cname,skin_name) VALUES("后裔","半神之弓"),("后裔","光辉之辰");
# 添加一个学员 新版本语法
insert into student set username='张三疯', sex='男', age=99

-- 查询
--ORDER BY id DESC 按id降序排序  ASC 升序  ORDER BY 排序
--WHERE cname="后裔" 条件语法
SELECT * FROM skin WHERE cname="后裔" ORDER BY id DESC;

# 查询指定数量的数据 select 字段, 字段, … from 表名 limit 开始索引,查询条数
# 从1 索引开始,查询1条同学表数据
select  * from student limit 1,1

# 查询SQL - 统计表数据条数 - count()
select  count(id) from student



-- 更新
# 一般需要加条件指定更新数据
UPDATE skin set skin_name="精灵王" WHERE id=1;

-- 删除
# 一般需要加条件指定删除数据
DELETE FROM skin WHERE id=1;

mysql模块

  • mysql模块(全小写),是一个操作MySQL数据库的第三方模块
  • 通过它,可以 在代码中 操作数据库

尝试

/**
 * mysql模块:
 * 作用:能够让我们通过js代码  轻松操作数据库(可以执行sql语句)。
 * 
 * 如何使用呢?
 * 1、初始化环境 npm init -y
 * 2、下载mysql包,npm i mysql
 * 3、在js文件引入mysql依赖包,开始使用 
 */ 

// mysql的使用
// 1、引入mysql
const mysql = require("mysql");

// 2、创建连接对象
const conn = mysql.createConnection({
    host: "localhost",  // 主机地址
    user: "root",       // 数据库用户名(在小皮软件上可以查看)
    password: "123456",   // 数据库密码(一定要字符串)
    database: "hero"    // 数据库名字
})

// 3、连接数据库服务器
conn.connect();

// 4、可以操作数据库了(执行sql语句)
// 查询数据
// conn.query("select * from skin", (err,result)=>{
//     console.log(result);
// })

// 插入数据
// conn.query("insert into skin(cname,skin_name) values('张三', '法外狂徒')", (err,result)=>{
//     if(err){
//         console.log( new Error(err.sqlMessage));
//         return
//     }
//     console.log(err,result);
// })
// 更新操作
// conn.query("update skin set cname='李四' where id=3",(err,result)=>{
//     if(err){
//         console.log(err.sqlMessage);
//         return
//     }
//     console.log(result);
// })

// 删除
conn.query("delete from skin where id=4",(err,result)=>{
    console.log(err,result);
})

// 5、关闭连接
conn.end()

使用

一般封装函数模块来调用

function query(sql,callback){
    // 1、导入mysql
    const mysql = require("mysql");
    // 2、创建连接对象
    const conn = mysql.createConnection({
        host: "localhost",
        user: "root",
        password: "123456",
        database: "hero"
    })
    // 3、连接数据库服务器
    conn.connect();

    // 4、操作数据库
    conn.query( sql, callback );

    // 5、结束连接
    conn.end()
}
// 6、导出
module.exports = query;
const query = require("./04-封装mysql语法");

// 调用封装的query函数 并查询数据 正常sql语法即可使用
query("select * from skin", (err,result)=>{
    console.log(err,result);
})