nodejs篇 express模块(3)接口框架搭建

目录

    • 前言
    • 搭建基础代码
    • 项目文件结构
    • 配置常用全局中间件
    • 构建路由
    • 提取控制器模块
    • 配置统一的错误管理中间件
    • 配置404
    • 创建数据模型
    • nodejs相关其它内容

前言

nodejs篇 express模块(3)接口框架搭建_第1张图片
通过前面两个章节express(1)基础 和 express(2)中间件的使用的学习,我们掌握了express的一些常见的语法和使用方式。现在我们来探究,express在实际项目中是如何开发的,或者说,如何通过express来搭建一个服务器项目。

搭建基础代码

在通过npm init -y 和安装好express之后,先在根目录下创建app.js,准备好服务器的基础代码

var express = require("express");

var app = express();
var PORT = process.env.PORT || 3000;

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

app.listen(PORT, function () {
  console.log(`server runing at ${PORT}`);
});

项目文件结构

    |-- config # 配置文件
		|-- configDefault.js # 配置文件	
    |-- controller # 用于解析用户的输入,处理后返回相应的结果
    	|-- user.js # 所有和user相关接口的通用处理函数或变量
    	|-- xxx.js # 不同的路由模块,业务放在不同的js文件中
    |-- model # 数据持久层 所有对数据的操作,封装到里面
    	|-- index.js # 组合所有业务的数据模型
    	|-- user.js # 与user业务相关的数据模型
    	|-- xxx.js # 其它业务相关的数据模型
    |-- middleware # 用于编写非路由的中间件,统一在里面管理
    	|-- errorHandler.js # 专门处理服务器报错的中间件
    |-- router # 把应用中所有的路由配置到里面
    	|-- index.js # 所有路由的模块最终都会被引入到index.js中
    	|-- user.js # user业务路由模块
    	|-- xxx.js # 其它业务路由模块	
    |-- util # 全局通用变量或者函数的封装
		|-- index.js # 全局通用变量或者函数的封装
    |-- app.js # 我们服务器的主文件

配置常用全局中间件

express.json 和 express.urlencoded这两个中间件用来解析用户请求
cors 用来处理客户端跨域请求
morgan用来打印请求日志
写代码之前,先分别安装cors和morgan这两个第三方中间件

npm install cors
npm install morgan

在之前的介绍中,提到过express.json 和 express.urlencoded是express自带的,不需要额外安装

// app.js
var express = require("express");
var morgan = require("morgan");
var cors = require("cors");

var app = express();
var PORT = process.env.PORT || 3000;

var middlewares = [express.json(), express.urlencoded()];

app.use(middlewares, morgan("dev"), cors());

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

app.listen(PORT, function () {
  console.log(`server runing at ${PORT}`);
});

构建路由

如文件结构所示,在根目录创建一个router文件见,并创建index.js

//router/index.js
var express = require("express");

// 创建路由实例
const router = express.Router();

// 配置路由
router.get("/", function (req, res) {
  res.status("200");
  res.json({ code: 200, data: "请求成功了" });
});

// 导出路由
module.exports = router;

在app.js中使用

//app.js
var express = require("express");
var morgan = require("morgan");
var cors = require("cors");

var router = require("./router");

var app = express();
var PORT = process.env.PORT || 3000;

var middlewares = [express.json(), express.urlencoded()];

app.use(middlewares, morgan("dev"), cors());

// 我们希望所有的接口都以api开头,符合restful接口开发规范
app.use("/api", router);

app.listen(PORT, function () {
  console.log(`server runing at ${PORT}`);
});

router/index.js中配置细化路由

我们不可能将整个项目的所有接口,都放入到router/index.js文件中,所以需要根据功能划分不同的模块,来开发配置路由。

比如与用户相关的接口user
在router文件下创建user.js

//router/user.js
var express = require("express");

// 创建路由实例
const router = express.Router();

// 用户登录
router.post("/user/login", function (req, res) {
  res.status("200");
  res.json({ code: 200, data: "请求成功了" });
});

// 用户注册
router.post("/users", function (req, res) {
  res.status("200");
  res.json({ code: 200, data: "请求成功了" });
});

// 获取用户
router.get("/user", function (req, res) {
  res.status("200");
  res.json({ code: 200, data: "请求成功了" });
});

// 更新用户
router.put("/user", function (req, res) {
  res.status("200");
  res.json({ code: 200, data: "请求成功了" });
});

// 导出路由
module.exports = router;

在router/index.js中引入

// router/index.js
var express = require("express");

var user = require("./user");

// 创建路由实例
const router = express.Router();

// 配置路由,遵循restful接口开发规范,统一使用json格式返回数据
router.use(function (req, res, next) {
  res.set("Content-Type", "application/json");
  next();
});

// 用户user相关的路由
router.use(user);

// 导出路由
module.exports = router;

一套路由配置的标准就建立得差不多了。

提取控制器模块

路由我们配置好了,可每一个接口的代码,处理客户端传递过来的数据,连接数据库或者转发到其它接口,或者是读取文件流等等,最终为本次请求提供响应,这一系列的操作,具体怎么实现?都放在接口router配置的回调函数里?

当然不是,很多时候,我们会将相同的方法,都配置到controller文件夹下,正如前面文件目录所示,在根目录下创建controller文件,并按照模块创建不同的js文件,比如user.js

// controller/user.js
exports.login = function (req, res) {
  res.status("200");
  res.json({
    code: 200,
    data: "请求成功了",
    path: "/api/user/login",
    methods: "post",
  });
};

exports.register = function (req, res) {
  res.status("200");
  res.json({
    code: 200,
    data: "请求成功了",
    path: "/api/users",
    methods: "post",
  });
};

exports.getUser = function (req, res) {
  res.status("200");
  res.json({
    code: 200,
    data: "请求成功了",
    path: "/api/user",
    methods: "get",
  });
};

exports.putUser = function (req, res) {
  res.status("200");
  res.json({
    code: 200,
    data: "请求成功了",
    path: "/api/user",
    methods: "put",
  });
};

在router/user.js中引入使用。

// router/user.js

var express = require("express");
var userControl = require("../controller/user");

// 创建路由实例
const router = express.Router();

// 用户登录
router.post("/user/login", userControl.login);

// 用户注册
router.post("/users", userControl.register);

// 获取用户
router.get("/user", userControl.getUser);

// 更新用户
router.put("/user", userControl.putUser);

// 导出路由
module.exports = router;

配置统一的错误管理中间件

在express(2)中,介绍了错误处理中间件的使用和注册方式,现在,我们要在真实的项目中启用。

如上的文件路径所示,这是一个中间件,所以放置在middleware文件夹下的errorHandler.js中。

// middleware/errorHandler.js
// err里面必须传入错误状态码和错误的信息
module.exports = function () {
  return function (err, req, res, next) {    
    res.status(err.status).json({
      message: err.message,
    });
  };
};

在全局的app.js中所有路由配置后,配置该中间件

// app.js
var express = require("express");
var morgan = require("morgan");
var cors = require("cors");

var router = require("./router");
var errHandler = require("./middleware/errorHandler");

var app = express();
var PORT = process.env.PORT || 3000;

var middlewares = [express.json(), express.urlencoded()];

app.use(middlewares, morgan("dev"), cors());

// 我们希望所有的接口都以api开头
app.use("/api", router);

app.use(errHandler())
 
app.listen(PORT, function () {
  console.log(`server runing at ${PORT}`);
});

当想要抛出错误时,如何使用,某个router的回调函数举例
还记得如何触发express的错误中间件么?next(params) params非字符串’route’即可

// controller/user.js
exports.putUser = function (req, res, next) {
  try {
    // ...
    res.status("200");
    res.json({
      code: 200,
      data: "请求成功了",
      path: "/api/user",
      methods: "put",
    });
  } catch (error) {
    console.log(error)
    next({
      status: 500,
      message: '该用户更新失败'
    })
  }
};

配置404

//app.js
var express = require("express");
var morgan = require("morgan");
var cors = require("cors");

var router = require("./router");
var errHandler = require("./middleware/errorHandler");

var app = express();
var PORT = process.env.PORT || 3000;

var middlewares = [express.json(), express.urlencoded()];

app.use(middlewares, morgan("dev"), cors());

// 我们希望所有的接口都以api开头
app.use("/api", router);

// 404中间件处理,由于之前的所有router都没匹配到,前面的中间件都没有回复响应,我们需要这个中间件做兜底处理。
app.use((req, res) => {
  res.status("404").send("404 not found");
});

app.use(errHandler());

app.listen(PORT, function () {
  console.log(`server runing at ${PORT}`);
});

创建数据模型

绝大多数业务都需要使用到数据库,创建数据模型。在model文件夹下创建index.js,用来链接所有的业务的数据模型,再以user业务为例,创建user.js文件。

以mysql数据库为例,需要安装npm第三方的包 mysql 和mysql2

npm install mysql mysql2

数据库的配置在一个项目中,通常都是固定的,host,user,password,database这些,我们可以放置在 config/configDefault.js文件中。

// config/configDefault.js
module.exports = {
    mysqlHost: '127.0.0.1',
    mysqlUser: 'dengxi',
    mysqlPassword: 'root',
    mysqlDatabase: 'express'
}
// model/index.js
var mysql = require("mysql2");
var {
  mysqlHost,
  mysqlUser,
  mysqlPassword,
  mysqlDatabase,
} = require("../config/configDefault");

var pool = mysql.createPool({
  host: mysqlHost, // host: 数据库服务器的主机名
  user: mysqlUser, // user: 连接数据库所需的用户名
  password: mysqlPassword, // password: 连接数据库所需的密码
  database: mysqlDatabase, // database: 要连接的数据库的名称
  waitForConnections: true, // waitForConnections: 如果连接池已满,等待连接变为可用的时间(以毫秒为单位)
  connectionLimit: 10, // connectionLimit: 连接池的大小
  queueLimit: 0, // queueLimit: 当连接池已满时,排队等待连接变为可用的最大连接数。如果设置为0,则不允许排队
});

// 将一个promise抛出
module.exports = pool;

在utils文件夹下,将连接池的逻辑封装成一个函数,作为常用工具使用

// utils/index.js
var pool = require('../model/index');

// node中使用回调的api大部分都是异步的,这里对操作数据库动作做下封装。
exports.RunSQL = async function (sql) {
  return new Promise((resolve, reject) => {
    pool.query(sql, (err, result) => {
      if (err) reject(err);
      resolve(result);
    });
  });
}

具体业务的使用以注册用户user业务为例
post接口 path=“/api/users” 接收两个参数 username 和 password

在model层处理用户传递过来的数据并校验,然后连接数据库,并处理结果返回

// model/user.js
var utils = require("../utils/index");

/**
 * 注册用户,model内部只做数据校验,和数据库连接,并返回结果给controller/user.js
 * */
exports.signupUser = async (req) => {
  var { username, password } = req.body;
  var userNameIsOnlySql = `SELECT * FROM node_user WHERE user_name = '${username}'`;
  var insertUserNameSql = `INSERT INTO node_user (user_name) VALUES ('${username}')`;
  var result = {};

  if (!(username && password)) {
    // next({
    //   status: 401,
    //   message: "用户名或密码信息不存在",
    // });
    return {
      type: "failed",
      status: "401",
      data: "",
      message: "用户名或密码信息不存在",
    };
  }


  try {
    // 执行插入前执行查一下该用户名是否被注册
    const queryRepeatUserList = await utils.RunSQL(userNameIsOnlySql);

    // 无结果证明没有被注册
    if (!queryRepeatUserList.length) {
      const sqlResult = await utils.RunSQL(insertUserNameSql);

      if (sqlResult) {
        result = {
          type: "success",
          data: JSON.stringify({ user_id: sqlResult.insertId }),
          message: "用户注册成功",
        };
      }
    } else {
      result = {
        type: "failed",
        data: "",
        status: "500",
        message: "用户名重复",
      };
    }
  } catch (error) {
    result = {
      type: "failed",
      data: "",
      status: "500",
      message: "数据库连接失败",
    };
  }

  return result;
};

在controller内部获取model层的结果,并做出接口响应

// controller/user.js
var { signupUser } = require("../model/user");

exports.register = async function (req, res, next) {
  var result = await signupUser(req);

  res.set('Content-Type', 'application/json')

  if (result.type === "success") {
    res.status("200");
    res.json({
      code: "200",
      data: "注册成功",
      path: "/api/users",
      methods: "post",
    });
  } else {
  	// 统一的错误处理中间件
    next({
      status: result.status,
      message: result.message
    })
  }
};

到现在为止,一整套express服务器项目流程就建立得七七八八了,稍后我会将完整的代码上传到代码仓库。

nodejs相关其它内容

nodejs commonjs介绍
nodejs fs模块介绍
nodejs path模块介绍
nodejs events模块介绍
nodejs http模块介绍
nodejs net模块介绍
nodejs url模块介绍
nodejs process模块介绍
nodejs buffer模块介绍
nodejs stream 模块介绍
nodejs express(1)模块介绍
nodejs express(2)中间件详解

你可能感兴趣的:(nodeJS,express,nodejs,javascript)