代码地址:https://github.com/changeclass/Node_Blog_express.git

# 脚手架(express-gemerator)

  1. 安装 express

    npm i express-generator -g
  2. 初始化目录

    express blog-express

    image-20201010164058825

  3. 安装插件

    cd blog-express
    yarn install
  4. 运行项目

    yarn run start

    或者 npm start

    image-20201010164951101

  5. 访问 http://127.0.0.1:3000

    image-20201010165017597

  6. 为了方便开发,继续安装 nodemoncross-env

    yarn add nodemon cross-env
  7. 接下来在 package.json 中设置 dev 环境下的启动命令。

    "scripts": {
        "dev":"cross-env NODE_ENV=dev nodemon ./bin/www"
      },

# express 的入口代码

这里指的是 app.js

var createError = require("http-errors");
var express = require("express");
var path = require("path");
// 用于处理 Cookie
var cookieParser = require("cookie-parser");
// 记录 access log
var logger = require("morgan");
// 引用两个路由
var indexRouter = require("./routes/index");
var usersRouter = require("./routes/users");
// 实例化 express
var app = express();
// 视图模板
app.set("views", path.join(__dirname, "views"));
app.set("view engine", "jade");
// 日志记录的级别
app.use(logger("dev"));
// 将数据转换为 JSON
app.use(express.json());
// 解析表单提交的数据
app.use(express.urlencoded({ extended: false }));
// Cookie
app.use(cookieParser());
// 设置静态目录
// app.use(express.static(path.join(__dirname, "public")));
// 定义路由 ///(indexRouter 里的路由)
app.use("/", indexRouter);
///users/(usersRouter 里的路由)
app.use("/users", usersRouter);
// 如果上面的路由无法匹配,则执行下面的路由 即 404
app.use(function (req, res, next) {
  next(createError(404));
});
// 如果遇到错误
app.use(function (err, req, res, next) {
  // set locals, only providing error in development
  res.locals.message = err.message;
  res.locals.error = req.app.get("env") === "development" ? err : {};
  // render the error page
  res.status(err.status || 500);
  res.render("error");
});
module.exports = app;

# 路由处理

express 中路由注册分为三步,分别为:引用路由、注册路由、在路由中实现逻辑。

  1. 引用路由

    即引用一个文件,这个文件里为具体的路由逻辑

    // 自己的路由
    var blogRouter = require("./routes/blog");
    var userRouter = require("./routes/user");
  2. 注册路由,即对当前的路由进行注册。

    app.use("/api/blog", blogRouter);
    app.use("/api/user", userRouter);

    这样处理后,路由逻辑的地址为 服务器/api/blog+路由的地址

  3. 实现具体路由

    var express = require("express");
    var router = express.Router();
    router.get("/list", (req, res, next) => {
      res.json({
        errno: 0,
        data: [1, 23, 4],
      });
    });
    module.exports = router;

    上述代码表示,注册一个 get 请求,请求路径为 /api/user/list 的路由。返回数据即回调函数内的数据。

# GET 请求

router.get("/list", (req, res, next) => {
  res.json({
    errno: 0,
    data: [1, 23, 4],
  });
});

image-20201011112524787

# POST 请求

router.post("/login", (req, res) => {
  const { username, password } = req.body;
  res.json({
    errno: 0,
    data: { username, password },
  });
});

image-20201011112621205

# 中间件

中间件就是请求路由时注册的函数。例如

app.use((req, res, next) => {
  console.log("处理404");
  res.json({
    errno: -1,
    msg: "404 Not found",
  });
});
app.post("/api/get-post-data", (req, res, next) => {
  console.log("post /api/get-post-data");
  res.json({
    errno: 0,
    data: req.body,
  });
});

通过 next() 方法即可使程序进行到下一步。例如在执行某程序之前需要先进行 Cookie 的设置或者异步请求某个数据以验证是否执行。可以使用 next()

app.use((req, res, next) => {
  // 假设处理 Cookie
  req.cookie = {
    userId: "123",
  };
  next();
});
app.get("/api/get-cookie", (req, res, next) => {
  console.log("get /api/get-cookie");
  res.json({
    errno: 0,
    data: req.cookie,
  });
});

image-20201021144003756

# 初始化项目

  1. 在脚手架初始化的项目中安装插件 mysqlxss

    yarn add mysql xss
  2. 将可复用的代码复用

    • /controller
    • conf
    • db
    • model

    image-20201021154604832

    将使用原生 JS 写的博客的代码直接复用即可。

  3. 测试路由

    首先测试 list 路由,编辑 /routers/blog.js

    router.get("/list", (req, res, next) => {
      let author = req.query.author || "";
      const keyword = req.query.keyword || "";
      // if (req.query.isadmin) {
      //   // 管理员验证
      //   const loginCheckRult = loginCheck(req);
      //   if (loginCheckRult) {
      //     // 未登陆
      //     return loginCheckRult;
      //   }
      //   // 强行查询自己的博客
      //   author = req.session.username;
      // }
      const result = getList(author, keyword);
      return result.then((listData) => {
        res.json(new SuccessModel(listData));
      });
    });

    以上代码同样复用于上一个项目的代码。但是没有实现登陆因此将其注释掉。

    image-20201021154821102

# express 处理 session

  1. 安装插件

    yarn add express-session
  2. 在处理路由之前处理 session

    // 处理 session
    app.use(
      session({
        secret: "asdjhkkask", // 加密的密匙
        cookie: {
          path: "/", // 默认配置 - 路径
          httpOnly: true, // 默认配置 - 不允许客户端修改
          maxAge: 24 * 60 * 60 * 1000, // 生效时间
        },
      })
    );

    此时在访问刚才的页面就会出现 Cookie

    image-20201021161238418

# 连接 redis

  1. 安装插件

    yarn add redis connect-redis
  2. 复用代码

    将 db 中编辑 redis.js 文件

    const redis = require("redis");
    const { REDIS_CONF } = require("../conf/db");
    // 创建客户端 第一个参数表示端口 第二个参数表示地址
    const redisClient = redis.createClient(REDIS_CONF.port, REDIS_CONF.host);
    // 如果发生错误
    redisClient.on("err", (err) => {
      console.error(err);
    });
    function set(key, val) {
      if (typeof val === "object") {
        val = JSON.stringify(val);
      }
      redisClient.set(key, val, redis.print);
    }
    function get(key) {
      return new Promise((resolve, reject) => {
        redisClient.get(key, (err, val) => {
          if (err) {
            reject(err);
            return;
          }
          // 如果没有此值
          if (val === null) {
            resolve(null);
            return;
          }
          // 如果是 JSON 格式对象那么转为 JSON 在返回,否则直接返回
          try {
            resolve(JSON.parse(val));
          } catch (ex) {
            resolve(val);
          }
        });
      });
    }
    module.exports = {
      set,
      get,
    };
  3. 修改 app.js 文件

    const session = require("express-session");
    const RedisStore = require("connect-redis")(session);
    const redisClient = require("./db/redis");
    const sessionStroe = new RedisStore({
      client: redisClient,
    });
    // 处理 session
    app.use(
      session({
        secret: "asdjhkkask", // 加密的密匙
        cookie: {
          path: "/", // 默认配置 - 路径
          httpOnly: true, // 默认配置 - 不允许客户端修改
          maxAge: 24 * 60 * 60 * 1000, // 生效时间
        },
        store: sessionStroe,
      })
    );

    完成以上即可将 session 数据同步到 redis

image-20201021182732537

# 登陆中间件

为了方便管理,在项目根目录新建一个文件 middleware 夹用于存放所有中间件。

const { ErrorModel } = require("../model/resModel");
module.exports = (req, res, next) => {
  if (req.session.username) {
    next();
    return;
  }
  res.json(new ErrorModel("未登陆"));
};

# 开发路由

# 获取列表

// 获取列表路由
router.get("/list", (req, res, next) => {
    let author = req.query.author || "";
    const keyword = req.query.keyword || "";
    if (req.query.isAdmin) {
        if (req.session.username == null) {
            res.json(new ErrorModel("未登录"));
        }
        return;
    }
    // 强制查询自己的博客
    author = req.session.username;
    const result = getList(author, keyword);
    return result.then((listData) => {
        res.json(new SuccessModel(listData));
    });
});

image-20201021192638151

# 获取博客详情

// 获取详情路由
router.get("/detail", function (req, res, next) {
  const result = getDetail(req.query.id);
  return result.then((data) => {
    res.json(new SuccessModel(data));
  });
});

image-20201021192723598

# 新建博客

新建博客需要使用登陆验证的中间件,因此需要引入上一步的登陆验证的中间件。

const loginCheck = require("../middleware/loginCheck");

接下来,只需要在中间件处调用这个函数就可。

// 新建
router.post("/new", loginCheck, (req, res, next) => {
    req.body.author = req.session.username;
    const result = newBlog(req.body);
    return result.then((data) => {
        res.json(new SuccessModel(data));
    });
});

image-20201021192914696

# 更新博客

更新博客同样需要登陆验证,实现方法与上一步同理

// 更新
router.post("/update", loginCheck, (req, res, next) => {
  const result = updataBlog(req.query.id, req.body);
  return result.then((val) => {
    if (val) {
      res.json(new SuccessModel(val));
    } else {
      res.json(new ErrorModel("更新博客失败"));
    }
  });
});

image-20201021192933688

# 删除博客

// 删除
router.post("/del", loginCheck, (req, res, next) => {
  const author = req.session.username;
  const result = delBlog(req.query.id, author);
  return result.then((val) => {
    if (val) {
      res.json(new SuccessModel());
    } else {
      res.json(new ErrorModel("删除失败"));
    }
  });
});

# 日志

通过修改 logger 的等级即可修改日志格式。参考:https://github.com/expressjs/morgan/

例如修改为 combined

image-20201021202038154

当部署线上时需要将日志持久化也就是存储到文件内。

// 日志记录的级别
const ENV = process.env.NODE_ENV; // 当前运行的环境
if (ENV !== "production") {
  app.use(
    // 开发环境 测试环境
    logger("dev", {
      stream: process.stdout,
    })
  );
} else {
  const logFileName = path.join(__dirname, "logs", "access.log");
  const writeStream = fs.createWriteStream(logFileName, {
    flags: "a",
  });
  app.use(
    // 线上环境
    logger("combined", {
      stream: writeStream,
    })
  );
}
更新于

请我喝[茶]~( ̄▽ ̄)~*

Dreamy.TZK 微信支付

微信支付

Dreamy.TZK 支付宝

支付宝