likes
comments
collection
share

Express 入门指南(详细)

作者站长头像
站长
· 阅读数 16

一、安装

  1. 安装node.js,准备node环境

  2. 创建新文件夹,运行 npm init -y 创建package.json

  3. 运行 yarn add nodemon -D(自动重启工具)

  4. 在package.json的scripts中增加 "dev":"nodemon app.js",

  5. 运行 yarn add express 安装express

  6. 项目根目录新建 app.js

const express = require("express");
const app = express();

app.get("/", (req, res) => {
  res.send("Hello World");
});

app.listen(4000, () => {
  console.log("server is running");
});

运行 npm run dev ,访问localhost:4000可以看到hello world

二、路由

  1. 项目根目录新建controllers文件夹,专门存放路由回调

新建controllers/loginController.js,登录逻辑

exports.register = function (req, res) {
  res.send("Got a POST request");
}

新建controllers/userController.js,用户逻辑

exports.list = function (req, res) {
  res.json({
    //发送json数据类型
    list: [
      {
        name: "12",
        id: 1,
      },
      {
        name: "1233",
        id: 2,
      },
    ],
  });
}

exports.deleteUser = function (req, res) {
  res.send("Got a DELETE request at /user"); //发送各种类型的响应
}
  1. 项目根目录新建routes文件夹,专门存放路由

新建routes/login.js,登录路由

const express = require("express");
const loginController = require('../controllers/loginController');

const router = express.Router(); //模块化路由

router.post("/register", loginController.register);

module.exports = router;

新建routes/user.js,用户路由

const express = require("express");
const userController = require("../controllers/userController");

const router = express.Router();

router.get("/list", userController.list);

router.delete("/user", userController.deleteUser);

module.exports = router;
  1. 新建routes/index.js,路由出口
const express = require("express");
const userRouter = require("./user");
const loginRouter = require("./login");

const router = express.Router();

router.use("/api", userRouter); // 注入用户路由模块
router.use("/api", loginRouter); // 注入登录路由模块

module.exports = router;
  1. 在app.js中引入
const express = require("express");
const routes = require("./routes"); //新增
const app = express();

app.use("/", routes); //新增

//删除
// app.get("/", (req, res) => {
//   res.send("Hello World");
// });

app.listen(4000, () => {
  console.log("server is running");
});

通过浏览器或postman 访问 http://localhost:4000/api/list 可以看到接口成功被调用

将controllers逻辑与routes路由拆分开,目的是为了结构更加清晰,并且可以复用controller

最终目录结构:

Express 入门指南(详细)

三、托管静态文件

新建public/images文件夹,存放一些图片,例如react.png

在app.js中增加


var path = require('path');

app.use(express.static(path.join(__dirname, 'public')));

访问 http://localhost:4000/images/react.png 可以看到图片显示成功

四、中间件

中间件是个函数,有三个参数 req,res,next

若非结束响应,中间件必须调用 next() 将控制传递给下一个中间件函数(否则请求将成为悬挂请求)

1. 自定义全局中间件

//在app.js中新增
var myLogger = function (req, res, next) {
  console.log("LOGGED");
  next();
};

app.use(myLogger);

//加在 app.use("/", routes); 之前,在路由之前注册

访问:http://localhost:4000/api/list 可以看到终端打印了日志 LOGGED

2. 路由中间件

//在routes/login.js中新增
function login_middleware(req, res, next) {
  console.log("中间件1");
  next(); //传递给下一步
}

function login_params(req, res, next) {
  let { name, password } = req.query;
  if (!name || !password) {
    //发送消息,结束响应,不需要再调用next
    res.json({
      message: "参数校验失败",
    });
  } else {
    next();
  }
}

router.post("/login", [login_middleware, login_params], (req, res, next) => {
  res.send("登录成功");
});

在postman中使用post访问:http://localhost:4000/api/login?name=1&password=2 ,请求成功,并可在终端中看到打印信息

这里为了方便演示,将回调函数和路由都放到了一起,实际工作中,可做上述的controllers与routes拆分处理

接下来介绍第三方中间件 body-parser

五、body-parser

上面的post请求参数是放在url上的,实际场景通常都在body上,而express直接通过req.body只能获取到undefined,因此需要 body-parser 第三方库

  1. 运行 yarn add body-parser

  2. 修改app.js

const bodyParser = require('body-parser'); 

// 解析application/x-www-form-urlencoded数据格式
app.use(bodyParser.urlencoded({extended: true}));

// 解析json数据格式
app.use(bodyParser.json()); 

//解析 text/plain 数据格式
app.use(bodyParser.text()); 

//加在 app.use("/", routes); 之前,在路由之前注册

提示:body-parser目前不支持解析contentType: multipart/form-data 的格式类型

  1. 修改routes/login.js
router.post("/login", (req, res, next) => {
  console.log("req", req.body);
  res.send("登录成功");
});

使用postman通过post请求x-www-form-urlencoded数据格式

Express 入门指南(详细)

使用postman通过post请求json数据格式

Express 入门指南(详细)

注意:选完raw后,一定要将右侧的Text换成JSON

在实际开发中,前端通过URLSearchParams发送x-www-form-urlencoded数据格式

const params = new URLSearchParams();
params.append('param1', 'value1');
params.append('param2', 'value2');

六、解决跨域 cors

  1. 安装 yarn add cors

  2. 修改app.js

//新增
const cors = require('cors'); 

app.use(cors()); 

//加在 app.use("/", routes); 之前,在路由之前注册
  1. 最终app.js文件
const express = require("express");
const bodyParser = require("body-parser"); // 引入body-parser模块
var path = require("path");
const cors = require("cors");

const routes = require("./routes");

const app = express();

app.use(express.static(path.join(__dirname, "public")));

// 解析form表单提交的数据application/x-www-form-urlencoded
app.use(bodyParser.urlencoded({ extended: true })); 
app.use(bodyParser.json()); // 解析json数据格式
app.use(bodyParser.text()); //解析 text/plain 数据格式

app.use(cors()); // 注入cors模块解决跨域

app.use("/", routes);

app.listen(4000, () => {
  console.log("server is running port");
});

七、文件上传 multer

  1. 运行 yarn add multer

  2. 运行 yarn add uuid

  3. 新建util/upload.js

const fs = require("fs");
const path = require("path");
const multer = require("multer");
const uuid = require("uuid");

const memoryDest = path.join(__dirname, "../public/images");

const storage = multer.diskStorage({
  // 文件存储位置
  destination: (req, file, cb) => {
    // 校验文件夹是否存在,如果不存在则创建一个
    const isExists = fs.existsSync(memoryDest);
    if (!isExists) {
      fs.mkdirSync(memoryDest);
    }
    cb(null, memoryDest);
  },
  filename: (req, file, cb) => {
    // 生成唯一文件名
    const uid = uuid.v1();
    // 获取文件扩展名
    let ext = path.extname(file.originalname);
    cb(null, uid + ext);
  },
});

// 过滤文件
function fileFilter(req, file, callback) {
  if (!file) {
    callback(null, false);
  } else {
    callback(null, true);
  }
}
const upload = multer({
  storage: storage,
  fileFilter: fileFilter,
  limits: {
    fileSize: 20 * 1024 * 1024,
  },
}).single("file"); //上传的fieldname必须为file

module.exports = upload;
  1. 修改routes/user.js
const upload = require("../util/upload");

router.post("/upload", upload, (req, res, next) => {
  // 存储后的文件信息在 req.file 中,此时文件已经存储到本地了。
  console.log(req.file);
  res.send("success");
});
  1. 前端代码

注意:name="file"必传,与上面single("file")对应

//ant组件
<Upload
  name="file"
  listType="picture-card"
  className="avatar-uploader"
  showUploadList={false}
  action='http://127.0.0.1:4000/api/upload'
>

//Element组件
<el-upload
name="file"
class="avatar-uploader"
action='http://127.0.0.1:4000/api/upload'
:show-file-list="false"
></Upload>

八、连接数据库

前提准备:

  1. 本地数据库安装

该教程网上的资源比较多,可自行百度,如果有遇到麻烦的,可在评论区留言,我再更一篇安装本地数据库的教程

  1. 运行本地数据库并登录

  2. 修改本地数据库加密方式

在cmd中运行

ALTER USER 'root'@'localhost' IDENTIFIED BY 'password' PASSWORD EXPIRE NEVER;

ALTER USER 'root'@'localhost' IDENTIFIED WITH mysql_native_password BY '123456';

FLUSH PRIVILEGES;

  1. 使用Navicat for MySQL新建连接

Express 入门指南(详细)

  1. 新建数据库

Express 入门指南(详细)

  1. 新建一个user表

id设为主键,并自动递增

Express 入门指南(详细)

填一些数据

Express 入门指南(详细)

express部分:

  1. 安装mysql:yarn add mysql

  2. 新建db/mysql.js 创建数据库连接池

const mysql = require("mysql");
const pool = mysql.createPool({
  connectionLimit: 10, //最大连接数,默认为10
  host: "localhost", // 数据库服务器地址
  port: 3306, //数据库端口
  user: "root", // 数据库的用户名
  password: "123456", // 数据库密码
  database: "manage", // 数据库名
});

class Mysql {
  constructor() {}
  query(sql) {
    return new Promise((resolve, reject) => {
      pool.getConnection(function (err, connection) {
        if (err) {
          reject(err);
          throw err; // not connected!
        }
        connection.query(sql, function (error, results, fields) {
          if (error) {
            reject(err);
            throw error;
          }
          connection.release(); //只是释放链接,在缓冲池,没有被销毁
          resolve(results);
        });
      });
    });
  }
}

module.exports = new Mysql();
  1. 新建service/userService.js 存放sql语句,实现语句的复用
//新增 
exports.userAll = `SELECT * FROM user;`
  1. 修改controllers/userController.js
const mysql = require("../db/mysql");
const userService = require("../service/userService");

exports.getuser = function (req, res) {
  mysql.query(userService.userAll).then((data) => {
    let jsonData = JSON.parse(JSON.stringify(data));
    res.json({
      data: jsonData,
    });
  });
};
  1. 修改routes/user.js
//新增
router.get("/user/json", userController.getuser);

使用浏览器或postman访问 http://localhost:4000/api/user/json 可以获取到数据

九、express-jwt、jsonwebtoken

jsonwebtoken是用来生成token给客户端的,express-jwt是用来验证token的

  1. 安装:yarn add jsonwebtoken yarn add express-jwt

  2. 修改routes/login.js 生成token

const jwt = require("jsonwebtoken");

router.post("/login", (req, res, next) => {
  let { username } = req.body;

  // 登录成功,签发一个token并返回给前端
  const token = jwt.sign(
    // payload:签发的 token 里面要包含的一些数据
    { username},
    // 私钥
    "caowj",
    // 设置过期时间
    { expiresIn: 60 * 60 * 24 } //1 day
  );

  res.json({
    msg: "登录成功",
    data: { token },
  });
});
  1. 新建util/user-jwt.js 验证token
var { expressjwt: jwt } = require("express-jwt");

// 验证token是否过期
const jwtAuth = jwt({
  secret: "caowj", //密匙
  algorithms: ["HS256"], //签名算法
}).unless({ path: ["/api/login", "/api/register"] }); // unless 设置jwt认证白名单

module.exports = jwtAuth;
  1. 修改routes/index.js 注入验证模块,并对错误进行处理
//注入验证模块
const jwtAuth = require("../util/user-jwt");

router.use(jwtAuth); 

router.use("/api", userRouter); // 注入用户路由模块
router.use("/api", loginRouter); // 注入登录路由模块

// 自定义统一异常处理中间件,需要放在代码最后
router.use((err, req, res, next) => {
  // 自定义用户认证失败的错误返回
  console.log("err===", err);
  if (err && err.name === "UnauthorizedError") {
    const { status = 401, message } = err;
    // 抛出401异常
    res.status(status).json({
      code: status,
      msg: "token失效,请重新登录",
      data: null,
    });
  } else {
    const { output } = err || {};
    // 错误码和错误信息
    const errCode = (output && output.statusCode) || 500;
    const errMsg =
      (output && output.payload && output.payload.error) || err.message;
    res.status(errCode).json({
      code: errCode,
      msg: errMsg,
    });
  }
});

使用postman访问/api/login获取到token,这样在请求别的接口时,需要在请求头上将token加到authorization上

//前端传的对应值以Bearer开头然后空一格,接近着是token值 

authorization:Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6I

该设计方式与express-jwt源码有关,感兴趣的可以去阅读

十、结尾

本篇文章如果有不对或不详细的地方,欢迎在评论区留言,后续会继续更新这篇文章

码字不易,点赞支持!!!

最后,祝大家春节快乐~

欢迎关注:之后文章会首发在云在前端公众号,未经许可禁止转载!