听说你还不会使用Express?

本文涉及的产品
云数据库 Tair(兼容Redis),内存型 2GB
RDS MySQL Serverless 基础系列,0.5-2RCU 50GB
Redis 开源版,标准版 2GB
推荐场景:
搭建游戏排行榜
简介: Express

简介

Express是一个基于 Node.js 平台,快速、开放、极简的 Web 开发框架。对于前端开发者想入门后端是一很不错的选择。但是 Express 初始化后,并不马上就是一个开箱即用,各种功能完善的 web 服务端项目,例如:日志记录、错误捕获、数据库连接、token认证 等一系列常见的功能,需要开发者自己去安装插件进行配置完善功能,如果你对 web 服务端开发或者 Express 框架不熟悉,那将是一项耗费巨大资源的工作。本文在 Express 的初始架构上配置了日志记录、错误捕获、数据库连接、静态目录、token认证 等一系列常见的功能,希望能减轻大家的工作量,更高效完成工作,有更多时间提升自己的能力。

本文适合有Express基础,急需需要搭建项目的同学食用,如果对Express完全不了解的建议先去看看Express官方文档。

在讲Express的使用之前,我们先来介绍一下非常出名的洋葱模型,这对后面代码的理解有很好的帮助。

洋葱模型

洋葱我相信大家都吃过,一层一层的,Express的中间件执行机制也是类似一个洋葱,所以叫洋葱模型。

image.png

下面笔者举个简单例子

有如下代码,一个全局中间件,一个单独中间件。我们浏览器执行请求,看控制台打印什么结果。

// 洋葱模型
// use使用中间件,这里是全局使用中间件
app.use(function (request, response, next) {
  console.log("start In comes a " + request.method + " to " + request.url);
  next();
  console.log("end In comes a " + request.method + " to " + request.url);
});

// 单独中间件
app.get(
  "/",
  async (req, res, next) => {
    console.log("我是单独中间件 start");
    next();
    console.log("我是单独中间件 end");
  },
  (req, res, next) => {
    console.log("send result start");
    res.send("get method!");
    console.log("send result end");
  }
);

可以看到它的执行机制,以next方法为分水岭,先执行所有中间件的next方法前半部分,然后从后往前执行中间件next方法的下半部分。这就是著名的洋葱模型。

image.png

上面的例子是同步代码,如果掺杂异步代码会怎么样呢?出于这个好奇心,我们再来测试一下。将上面的代码改造下,在单独的中间件中加入异步代码。

// 单独中间件
app.get(
  "/",
  async (req, res, next) => {
    console.log("我是单独中间件 start");
    next();
    // 异步代码
    const result = await Promise.resolve(123);
    console.log(result);
    console.log("我是单独中间件 end");
  },
  (req, res, next) => {
    console.log("send result start");
    res.send("get method!");
    console.log("send result end");
  }
);

我们来执行下,看下结果,可以看到顺序被打乱了。

按理来说,最后应该是先执行123 -> 我是单独中间件 end -> end In comes a Get to /,但是并没有,而是先回到了全局中间件执行,然后再执行单独的中间件。

image.png

我们仔细研究可以发现,await代码会阻塞当前中间件后续代码的执行,但是不会阻塞前面其它中间件的执行。这个特点在Express中需要格外注意。

为了印证我们的猜想,我们再来测试下,将异步代码换到next方法的上面。

// 单独中间件
app.get(
  "/",
  async (req, res, next) => {
    console.log("我是单独中间件 start");
    // 异步代码
    const result = await Promise.resolve(123);
    console.log(result);
    next();
    console.log("我是单独中间件 end");
  },
  (req, res, next) => {
    console.log("send result start");
    res.send("get method!");
    console.log("send result end");
  }
);

我们猜想下输出,应该是start In comes a Get to / -> 我是单独中间件 start -> end In comes a Get to / -> 123 -> send result start -> send result end -> 我是单独中间件 end

我们执行下请求,看下结果

image.png

结果吻合,印证了我们的猜想,异步代码会阻塞当前中间件后续代码的执行,但是不会阻塞其它中间件的执行。

好啦,下面我们正式步入正题,来讲解Express的使用。

创建应用

首先我们需要安装

npm i express

然后引入使用就可以了

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

app.listen(3000, () => {
  console.log("serve running on 3000");
});

路由

Express是自带路由体系的,不需要借助第三方插件,并且app是直接支持路由的。

app.get("/user1", (req, res) => {
  res.send(req.method + req.url);
});

app.post("/user2", (req, res) => {
  res.send(req.method + req.url);
});

app.put("/user3", (req, res) => {
  res.send(req.method + req.url);
});

app.delete("/user4", (req, res) => {
  res.send(req.method + req.url);
});

// 所有请求都支持
app.all("/user5", (req, res) => {
  res.send(req.method + req.url);
});

// 重定向
app.get("/testredirect", (req, res) => {
  res.redirect("/user1");
});

这样我们就可以通过localhost:3000/xxx来调用接口了。

路由分模块

如果路由很多,想分模块可以使用express.Router()方法来分模块。

// routes/user.js
const express = require("express");
const router = express.Router();

router.get("/select", (req, res, next) => {
  res.end("get");
});

router.post("/add", (req, res, next) => {
  res.end("post");
});

router.delete("/delete", (req, res, next) => {
  res.end("deleted");
});

router.put("/update", (req, res, next) => {
  res.end("put");
});

router.all("/userall", (req, res) => {
  res.send("所有请求都可以?" + req.method);
});

module.exports = router;

在入口文件,我们将路由通过中间件注册就可以了。

// index.js
const userRouter = require("./routes/user");
app.use("/user", userRouter);

这样我们就可以通过localhost:3000/user/xxx来调用接口了。

自动注册路由

如果模块很多的话,不想每次都去手动在入口文件注册,我们还可以优化,通过fs模块读取文件,自动完成路由的注册。

// routes/index.js
const fs = require("fs");
const path = require("path");

// 批量注册路由
module.exports = (app) => {
  fs.readdirSync(__dirname).forEach((file) => {
    if (file === "index.js") {
      return;
    }
    const route = require(`./${file}`);
    app.use(`/${path.basename(file, ".js")}`, route);
  });
};

在入口文件,我们可以通过该方法批量注册路由了

const registerRoute = require("./routes/index");
registerRoute(app);

这样我们就可以通过localhost:3000/模块名/xxx来调用接口了。

路由说完了,我们再来看看怎么获取参数。

参数获取

参数的获取分为query、param、body三种形式。

query参数

对于query参数,通过req.query获取

app.get("/user", (req, res) => {
  const params = req.query;
  res.send(params);
});

我们来测试一下,参数能正常获取

image.png

我们再来看看路径参数

路径参数

对于路径参数,通过:变量定义,然后通过params获取。

app.get("/user2/:name/:age", (req, res) => {
  // 路径参数获取
  const params = req.params;
  res.send(params);
});

我们来测试一下,参数都能正常获取

image.png

body参数

对于body参数,也就是请求体里面的参数,Express4.16之前需要借助body-parser插件,但是后面的版本就原生支持了。

// 老版本写法 不推荐
// var bodyParser = require("body-parser");
// app.use(bodyParser.urlencoded({ extended: false }));
// app.use(bodyParser.json());

// 4.16以后的写法 推荐
app.use(express.urlencoded({ extended: false }));
app.use(express.json());

然后通过req.body获取参数。

app.post("/user", (req, res) => {
  const params = req.body;
  res.send(params);
});

设置完后,我们就可以获取到请求体里面的参数了。

image.png

文件上传

说完参数的获取,我们再来看看怎么处理文件上传。

文件上传需要借助第三方插件multer

首先我们来安装npm i multer

然后再入口文件使用

// index.js
// 自定义存储,比如重命名
const storage = multer.diskStorage({
  destination: function (req, file, cb) {
    // 注意这种方式文件夹必须事先存在
    cb(null, "uploads");
  },
  filename: function (req, file, cb) {
    console.log(file);
    const uniquePrefix = Date.now() + "-" + Math.round(Math.random() * 1e9);
    cb(null, uniquePrefix + "-" + file.originalname);
  },
});

const upload = multer({ storage: storage });

单文件上传

对于单文件上传,有两种形式,一种是没有表单字段名,另外一种是有表单字段名。

对于单文件,我们通过req.file获取

// 单文件上传,不固定表单字段名字
app.post("/file", upload.single(), (req, res) => {
  res.json(req.file);
});

我们来测试下,图片上传成功

image.png

我们再来看看带表单字段名的上传

// 字单文件上传,字段名必须为avatar
app.post("/file2", upload.single("avatar"), (req, res) => {
  res.json(req.file);
});

图片也上传成功

image.png

多文件上传

多文件分有表单字段名,单表单字段名,多表单字段名三种。对于多文件,我们通过req.files来获取

我们先来看看没有表单字段名的。

// 多文件上传,不固定表单字段名字
app.post("/files", upload.array(), (req, res) => {
  res.json(req.files);
});

可以看到,它返回的是一个数组

image.png

我们再来看看单字段名的

// 多文件上传,段名必须为avatars,不超过三张
app.post("/files2", upload.array("avatars", 3), (req, res) => {
  res.json(req.files);
});

可以看到,它返回的是一个数组,并且他会多一个叫fieldname的字段,就是我们的表单字段名。

image.png

我们再来看看多字段名

// 多文件上传,段名必须为avatar、banner,不超过2张的3张
app.post(
  "/files3",
  upload.fields([
    { name: "avatar", macCount: 2 },
    { name: "banner", maxCount: 3 },
  ]),
  (req, res) => {
    res.json(req.files);
  }
);

可以看到,他返回的是一个对象,对象里面的属性值是数组。并且他会多一个叫fieldname的字段,就是我们的表单字段名。

image.png

静态目录

前面我们上传的文件是不能访问的,如果想访问该怎么办呢?那就是静态目录了。

通过下面的配置,我们访问localhost:3000/static就会访问到系统uploads目录。

// 静态资源处理
app.use("/static", express.static(path.join(__dirname, "uploads")));

前面我们上传的文件都是在uploads下,我们随便拿个文件来测试下

image.png

可以看到,图片正常展示出来了。

错误处理

错误的处理,对于一个系统的稳定性也是极其重要的一步。对于错误如果不处理对于用户是非常不友好的。

比如下面的例子

// 错误处理,模拟错误
app.get("/error", function (req, res, next) {
  // 同步错误可以直接捕获
  throw new Error("抛出一个错误");
});

当用户访问的时候,会莫名其妙的显示一大串错误。

image.png

同步错误

对于错误处理,很多小伙伴可能或想到try catch,对try catch确实可以解决这个问题,但是如果我们的接口有很多,我们每个接口都要去写try catch吗,有没有统一的处理方法,诶,还真有。

Express给我们提供了一个错误中间件,用来统一处理错误,注意这个中间件需要放到最后。

我们加上中间件来测试一下效果

// 错误处理中间件,定义在最后
app.use((err, req, res, next) => {
  res.status(err.status || 500).json({
    message: err.message || "服务端错误",
  });
});

可以看到,我们的错误提示就非常友好了。

image.png

异步错误

但是对于异步错误,我们需要注意,要在next中抛出错误,否则是捕获不到的。

app.get("/error2", function (req, res, next) {
  // 异步错误需要显示调用next
  setImmediate(() => {
    // 报告异步错误必须通过 next()
    next(new Error("异步抛出一个错误"));
  });
});

放在next中,异步错误被正常捕获。

image.png

日志

对于线上项目用来说,日志是非常重要的一环。log4js是使用得比较多的一个日志组件,经常跟Express一起配合使用。本文简单讲解下在Express怎么使用log4js

我们首先来安装该插件,笔者这里安装的版本是6.8.0

npm install log4js

然后我们创建一个utils文件夹下创建log.js,用来创建一个logger

// utils/log.js

const log4js = require("log4js");
const logger = log4js.getLogger();

logger.level = "debug"; // 需要打印的日志等级

module.exports = logger;

在需要的地方引入logger就可以了,我们来测试下

app.get("/logtest", (req, res) => {
  logger.debug("Some debug messages");
  logger.info("Some info messages");
  logger.warn("Some warn messages");
  logger.error("Some error messages");
  res.send("test log");
});

可以看到,日志都打印出来了

image.png

日志等级

我们再来改变下输出日志的等级

logger.level = "warn"; // 需要打印的日志等级

再来测试下,发现只输出了warnerror等级的日志,debuginfo等级的过滤掉了。

image.png

日志输出到文件

日志如果想输出到文件,我们还可以配置log4js

const log4js = require("log4js");

log4js.configure({
  appenders: { test: { type: "file", filename: "applog.log" } },
  categories: { default: { appenders: ["test"], level: "warn" } },
});

const logger = log4js.getLogger();

module.exports = logger;

我们再来测试下,发现它自动创建了applog.log文件,并将日志写入到了里面。

image.png

连接数据库

数据库目前主要有关系型数据库、非关系型数据库、缓存数据库,这三种数据库我们各举一个例子。

连接mongodb

为了方便操作mongodb,我们使用mongoose插件

首先我们来安装

npm  i mongoose

安装完后我们先创建db文件夹,然后创建mongodb.js,在这里来连接我们的mongodb数据库

// db/mongodb.js

const mongoose = require("mongoose");

module.exports = () => {
  // 数据库连接
  return new Promise((resolve, reject) => {
    mongoose
      .connect("mongodb://localhost/ExpressApi", {
        // useNewUrlParser: true,
        // useUnifiedTopology: true,
        // useFindAndModify: false,
      })
      .then(() => {
        console.log("mongodb数据库连接成功");
        resolve();
      })
      .catch((e) => {
        console.log(e);
        console.log("mongodb数据库连接失败");
        reject();
      });
  });
};

然后在我们的入口文件引用使用

// index.js

// 连接mongodb
const runmongodb = require("./db/mongodb.js");
runmongodb();

保存,我们运行一下,可以看到mongodb连接成功。

image.png

我们查看mongodb面板,可以看到ExpressApi数据库也创建成功了

image.png

数据库连接成功了,下面我们正式来创建接口。

我们以mvc模式,创建model、controller、route三个文件夹分别来管理模型、控制器、路由。

项目总体目录如下

model // 模型
controller // 控制器
route // 路由
db // 数据库连接
index.js // 入口文件

创建接口总共分为四步

  1. 创建模型
  2. 创建控制器
  3. 创建路由
  4. 使用路由

我们先来创建一个user model

// model/user.js
const mongoose = require("mongoose");
// 建立用户表
const UserSchema = new mongoose.Schema(
  {
    username: {
      type: String,
      unique: true,
    },
    password: {
      type: String,
      select: false,
    },
  },
  { timestamps: true }
);

// 建立用户数据库模型
module.exports = mongoose.model("User", UserSchema);

然后创建user控制器,定义一个保存和一个查询方法。

// controller/userController.js
const User = require("../model/user");

class UserController {
  async create(req, res) {
    const { username, password } = req.body;
    const repeatedUser = await User.findOne({ username, password });
    if (repeatedUser) {
      res.status(409).json({
        message: "用户已存在",
      });
    } else {
      const user = await new User({ username, password }).save();
      res.json(user);
    }
  }

  async query(req, res) {
    const users = await User.find();
    res.json(users);
  }
}

module.exports = new UserController();

然后我们在路由里面定义好查询和创建接口

// route/user.js

const express = require("express");
const router = express.Router();
const { create, query } = require("../controller/userController");

router.post("/create", create);
router.get("/query", query);

module.exports = router;

最后我们在入口文件使用该路由,前面我们说啦,路由少可以一个一个引入使用,对于路由多的话还是推荐使用自动注入的方式。

为了方便理解,这里我们还是使用引入的方式

// index.js

const userRouter = require("./routes/user");
app.use("/user", userRouter);

好啦,通过这四步,我们的接口就定义好啦,我们来测试一下

先来看看新增,接口正常返回

image.png

我们来看看数据库,发现user表添加了一条新记录。

image.png

我们再来看看查询接口,数据也能正常返回。

image.png

至此,我们的mongodb接口就创建并测试成功啦。

连接mysql

为了简化我们的操作,这里我们借助了ORM框架sequelize

我们先来安装这两个库

npm i mysql2 sequelize

然后在db目录下创建mysql.js用来连接mysql

const Sequelize = require("sequelize");

const sequelize = new Sequelize("ExpressApi", "root", "123456", {
  host: "localhost",
  dialect: "mysql",
});

// 测试数据库链接
sequelize
  .authenticate()
  .then(() => {
    console.log("数据库连接成功");
  })
  .catch((err) => {
    // 数据库连接失败时打印输出
    console.error(err);
    throw err;
  });

module.exports = sequelize;

这里要注意,需要先把数据库koaapi提前创建好。它不会自动创建。

跟前面一样,创建接口总共分为四步

  1. 创建模型
  2. 创建控制器
  3. 创建路由
  4. 使用路由

首先我们创建model,这里我们创建user2.js

// model/user2.js

const Sequelize = require("sequelize");
const sequelize = require("../db/mysql");

const User2 = sequelize.define("user", {
  username: {
    type: Sequelize.STRING,
  },
  password: {
    type: Sequelize.STRING,
  },
});

//同步数据库:没有表就新建,有就不变
User2.sync();

module.exports = User2;

然后创建控制器,定义一个保存和一个查询方法。

// controller/user2Controller.js

const User2 = require("../model/user2.js");

class user2Controller {
  async create(req, res) {
    const { username, password } = req.body;

    try {
      const user = await User2.create({ username, password });
      res.send(user);
    } catch (error) {
      console.log(e);
      res.json({ code: 0, message: "保存失败" });
    }
  }

  async query(req, res) {
    const users = await User2.findAll();
    res.json(users);
  }
}

module.exports = new user2Controller();

然后定义两个路由

const express = require("express");
const router = express.Router();
const { query, create } = require("../controller/user2Controller");

// 获取用户
router.get("/query", query);
// 添加用户
router.post("/create", create);

module.exports = router;

最后在入口文件使用该路由

// index.js

const user2Router = require("./routes/user2");
app.use("/user2", user2Router);

好啦,通过这四步,我们的接口就定义好啦,我们来测试一下

先来看看新增,接口正常返回

image.png

我们来看看数据库,发现users表添加了一条新记录。

image.png

我们再来看看查询接口,数据也能正常返回。

image.png

至此,我们的mysql接口就创建并测试成功啦。

我们再来看看缓存数据库redis

连接redis

这里我们也需要借助node-redis插件

我们先来安装

npm i redis

然后在db目录下创建redis.js用来连接redis

// db/redis.js

const { createClient } = require("redis");

const client = createClient();

// 开启连接
client.connect();

// 连接成功事件
client.on("connect", () => console.log("Redis Client Connect Success"));
// 错误事件
client.on("error", (err) => console.log("Redis Client Error", err));

module.exports = client;

然后我们创建一个简单的路由来测试一下

// route/dbtest

const express = require("express");
const router = express.Router();
const client = require("../db/redis");

router.get("/redis", async (req, res) => {
  await client.set("name", "randy");
  const name = await client.get("name");
  res.json({ name });
});

module.exports = router;

然后把该路由在入口文件注册使用

// index.js

const dbtestRouter = require("./routes/dbtest");
app.use("/dbtest", dbtestRouter);

最后我们来测试下接口,可以看到接口正常返回

image.png

我们再来查看一下我们的redis数据库,发现数据保存成功。

image.png

当然,这里只是一个简单的入门,redis的操作还有很多,大家可以看官方文档,这里笔者就不再详细说啦。

token验证

对于token的认证,我们这里使用目前比较流行的方案 jsonwebtoken

生成token

我们首先安装jsonwebtoken

npm i jsonwebtoken

安装完后,我们来实现一个登录接口,在接口里生成token并返回给前端。

注意这里因为是演示,所以将秘钥写死,真实项目最好从环境变量里面动态获取。

// route/user.js
const jwt = require("jsonwebtoken");

// ...
async login(req, res) {
  const { username, password } = req.body;
  const user = await User.findOne({ username, password });
  if (user) {
    // 生成token,有效期60秒
    const token = jwt.sign(
      { id: user.id, username: user.username },
      "miyao", // 这里的秘钥一般通过环境变量传递过来
      { expiresIn: 60 } // 有效期60秒
    );

    res.json({
      token,
    });
  } else {
    res.status(401).json({
      message: "账号或密码错误",
    });
  }
}

// ...

这里生成token的接口我们就定义好了,我们来测试一下。

首先输入错误的账号,看到它提示账号密码错误了

image.png

然后我们输入正确的账号密码试一下,可以看到,token被正常返回出来了。

image.png

到这里我们通过jsonwebtoken生成token就没问题了。接下来就是怎么验证token了。

token解密

在说token验证前,我们先来说个token解密,一般来说token是不需要解密的。但是如果非要看看里面是什么东西也是有办法解密的,那就得用到jwt-decode插件了。

该插件不验证密钥,任何格式良好的JWT都可以被解码。

我们来测试一下,

首先安装该插件

npm i jwt-decode

然后在登录接口里面使用jwt-decode解析token

const decoded = require("jwt-decode");

async login(req, res) {
  // ...
  console.log("decoded token", decoded(token));
  // ...
}

可以看到,就算没有秘钥也能将我们的token正确解析出来。

image.png

这个插件一般在我们前端用的比较多,比如想解析token,看看里面的数据是什么。它并不能验证token是否过期。如果想验证token的话还得使用下面的方法。

token验证

Express中,验证token是否有效我们一般会选择express-jwt插件。

下面笔者来演示下怎么使用

首先还是安装

npm i express-jwt

然后在入口文件以全局中间件的形式使用。

这个中间件我们要尽量放到前面,因为我们要验证所有接口token是否有效。

然后记得和错误中间件结合使用。

如果有些接口不想验证,可以使用unless排除,比如登录接口、静态资源。

// index.js
const { expressjwt } = require("express-jwt");

// token验证全局中间件,定义在前面
app.use(
  expressjwt({ secret: "miyao", algorithms: ["HS256"] }).unless({
    path: ["/user/login", "/static"], // 排除不需要验证的接口地址
  })
);

// 错误处理中间件,定义在最后
app.use((err, req, res, next) => {
  res.status(err.status || 500).json({
    message: err.message || "服务端错误",
  });
});

下面我们测试下,

我们先来看看不要token的接口,来访问一个静态资源。可以看到,没有token能正常获取资源。

image.png

我们再来访问一个需要token的接口,可以看到它提示错误了,说是没有token

image.png

我们用登录接口生成一个token,然后给该接口加上来测试下,可以看到接口正常获取到数据了。

image.png

因为我们的token设置了一分钟有效,所以我们过一分钟再来请求该接口。可以看到,它提示token过期了。

image.png

并且,token验证通过后,他会自动在request.auth里面加入token解析后的值。我们可以直接使用。

我们来测试一下,在userController里面添加testData方法,用来获取解析出来的token数据

// route/user.js

// ...
async testData(req, res) {
  res.json(req.auth);
}

我们调用接口测试下,可以看到数据正常获取了

image.png

好啦,关于token验证我们就讲到这里。

启动

node中,一般我们会使用node xx.js来运行某js文件。这种方式不仅不能后台运行而且如果报错了可能直接停止导致整个服务崩溃。

PM2Node 进程管理工具,可以利用它来简化很多 Node 应用管理的繁琐任务,如性能监控、自动重启、负载均衡等,而且使用非常简单。

首先我们需要全局安装

npm i pm2 -g

下面简单说说它的一些基本命令

  1. 启动应用:pm2 start xxx.js
  2. 查看所有进程:pm2 list
  3. 停止某个进程:pm2 stop name/id
  4. 停止所有进程:pm2 stop all
  5. 重启某个进程:pm2 restart name/id
  6. 删除某个进程:pm2 delete name/id

比如我们这里,启动当前应用,可以看到它以后台的模式将应用启动起来了。

image.png

当然关于pm2的使用远不止如此,大家可以查看PM2 文档自行学习。

系列文章

Node.js入门之什么是Node.js

Node.js入门之path模块

Node.js入门之fs模块

Node.js入门之url模块和querystring模块

Node.js入门之http模块和dns模块

Node.js入门之process模块、child_process模块、cluster模块

听说你还不会使用Express?

听说你还不会使用Koa?

后记

感谢小伙伴们的耐心观看,本文为笔者个人学习笔记,如有谬误,还请告知,万分感谢!如果本文对你有所帮助,还请点个关注点个赞~,您的支持是笔者不断更新的动力!

相关文章
|
6月前
|
JavaScript 前端开发 关系型数据库
玩转Express(一)实战开发
玩转Express(一)实战开发
|
5月前
一文搞懂:使用throng运行express应用
一文搞懂:使用throng运行express应用
23 0
|
JavaScript 前端开发 中间件
Express 基础操作和详解
Express.js 是一个基于 Node.js 平台的 Web 应用程序框架,它旨在帮助开发者轻松构建和管理 Web 服务和应用程序。Express 提供了许多强大的功能,使得创建可扩展、灵活且高性能的 Web 应用变得相对容易。以下是 Express 的主要特点和一些核心概念的详细解释
97 0
|
JavaScript 前端开发 中间件
如何用Express实现一个ADUS项目
如何用Express实现一个ADUS项目
79 0
|
存储 开发框架 JavaScript
Express框架的学习介绍
Express框架的学习介绍
104 0
|
缓存 NoSQL JavaScript
|
JavaScript 前端开发
Express基础(中)
Express基础(中)
132 0
Express基础(中)
|
中间件
express学习1-中间件应用
express学习1-中间件应用
122 3
express学习1-中间件应用
|
开发框架 JavaScript 前端开发
Express基础(上)
Express基础(上)
166 0
|
JSON 中间件 数据格式
彻底学懂express框架
彻底学懂express框架
258 0
彻底学懂express框架