Express

yuhuo2022-03-28开发库NodeJs库
参考链接

一. 指南

构建服务

// server.js
const path = require("path");
const express = require("express");
const app = express();

// get请求
app.get("/get", (req, res) => {
    // 设置状态码,返回html
    res.status(404).send("HTML代码");
});

// post请求
app.post("/post", (req, res) => {
    // 返回文件
    res.sendFile(path.resolve("avatar.jpg"));
});

// put请求
app.put("/put", (req, res) => {
    // 返回json
    res.json({params: request.params});
});

// delete请求
app.delete("/delete", (req, res) => {
    // 渲染html模板并返回
    res.render("example/get.hbs", { name: "张三" });
});

// 所有请求类型
app.all(/.*all$/, (req, res) => {
    // 设置响应头
    res.header('Access-Control-Allow-Origin', '*');
});

// 开启服务
app.listen(3000, () => {
    console.log(`Example app listening on port 3000`);
});

全局中间件

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

// 全局中间件
app.use((req, res, next) => {
    console.log("全局路由中间件");
    next();
});

// 根据路径匹配请求,请求类型不限(似乎跟app.all()一样)
app.use('/all', (req, res) => {
    res.send("HTML代码");
});

// 设置静态资源目录
// 通过 http://localhost:3000/a.jpg 直接访问 ./public/a.jpg
app.use(express.static('./public'));

// 可以在最后匹配任意路径*,处理404
app.use('*', (req, res) => {
    res.status(404).send("404啦!");
});

// 全局错误处理中间件
app.use((err, req, res, next) => {
  	console.error(err.stack);
  	res.status(500).send("500啦!");
});

说明

express 服务由各种中间件组成,包括 app.get()app.post() 等方法。当请求到达时,会严格按照中间件的注册顺序依次匹配,因此顺序也很重要。比如以下情况:

  • 全局中间件放在后面,可能前面的中间件匹配完就直接返回了,导致没有执行;
  • 全局错误中间件放在前面,当发送错误时往后匹配已经匹配不到了;
  • 匹配任意路径*的中间件放在前面,并直接返回,导致其后面的所有中间件都不会执行;

路由中间件

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

// 路由全局中间件
router.use((req, res, next) => {
    console.log("路由组中间件");
    next();
});

// 单路由中间件,语法同app,访问时需要加路由前缀,如:/animal/bird
router.get("/bird", (req, res) => {
    res.send("小鸟");
});

router.get(
    "/bird/:id",
    (req, res, next) => {
        console.log("单路由中间件");
        next();
    },
    (req, res) => {
        res.send("小鸟");
    }
);

// 添加路由中间件
app.use('/animal', router);

body中间件

解析请求中的body

const express = require("express");
const cookieParser = require("body-parser");
const app = express();

// 解析 contentType=text/plain 的请求体(文本字符串)
app.use(bodyParser.text());

// 解析 contentType=application/json 的请求体(json字符串,如 {"a":1,"b":2})
app.use(bodyParser.json());

// 解析 contentType=application/x-www-form-urlencoded 的请求体(url字符串,如 a=1&b=2)
app.use(bodyParser.urlencoded());

app.post("/post", (request, response) => {
    // 获取body
    console.log(request.body)
});

cookie中间件

解析请求中的cookie

const express = require("express");
const cookieParser = require("cookie-parser");
const app = express();

// 解析出请求中的cookie
app.use(cookieParser());

app.get("/get", (request, response) => {
    // 获取cookie
    console.log(request.cookies["name"])
});

文件上传

const express = require("express");
const path = require("path");
const fs = require("fs");
const uuid = require("uuid");
const Multer = require("multer");
const bodyParser = require("body-parser");

const app = express();
const publicPath = __dirname.replace(/\\/g, "/");
const uploadPath = `${publicPath}/upload`;
const uploadUrl = "http://localhost:3000/upload";

// 注册bodyParser中间件,解析request.body
app.use(bodyParser.json());

// 跨域设置
app.use("*", (request, response, next) => {
    response.header("Access-Control-Allow-Origin", "*");
    response.header("Access-Control-Allow-Headers", "content-type");
    next();
});

// 自动保存上传文件
const multer = Multer({
    storage: Multer.diskStorage({
        // 存放路径
        destination: "D:/uploads/",    
        // 自定义文件名
        filename: function (req, file, cb) {
            cb(null, file.fieldname + "-" + uuid.v4() + path.extname(file.originalname));
        },
    }),
});

// 上传单头像
app.post("/uploadAvatar", multer.single("avatar"), (request, response) => {
    response.json({ file: request.file, body: request.body });
});

// 上传多照片
app.post("/uploadPhoto", multer.array("photo"), (request, response) => {
    response.json({ files: request.files, body: request.body });
});

// 上传所有
const fields = multer.fields([{ name: "avatar" }, { name: "photo" }]);
app.post("/uploadAll", fields, (request, response) => {
    response.json({ files: request.files, body: request.body });
});

// 上传切片接口
app.post("/uploadChunk", Multer().single("chunkFile"), (request, response) => {
    const { chunkName } = request.body;
    const hash = chunkName.substring(0, chunkName.indexOf("_"));
    // 切片的保存目录
    const chunkPath = `${uploadPath}/${hash}`;
    !fs.existsSync(chunkPath) ? fs.mkdirSync(chunkPath) : null;
    // 手动保存切片
    fs.writeFileSync(`${chunkPath}/${chunkName}`, request.file.buffer);
    response.json({ status: 200 });
});

// 静态资源目录
app.use(express.static(publicPath));

app.listen(3000, () => {
    console.log(`Example app listening on port 3000`);
});

运行

单次运行

node server.js

监听重启

安装 nodemon

npm install nodemon

配置通过 nodemon 运行脚本

// package.json
{
	"scripts": {
    	"server": "nodemon server.js",
  	},
}

启动 nodemon

npm run server

二. 对象

Request

属性

属性类型描述
pathstring路径(/get)
protocolstring协议(http)
hostnamestring主机名(localhost)
paramsobject路径参数对象
queryobject查询参数对象
cookiesstringcookies(需要经过 cookieParser 中间件解析)
bodystring请求体
ipstringIP 地址

方法

方法描述
get(key: string): string返回指定的HTTP请求报头字段(不区分大小写)
accepts(): string[]返回 accept 数组

Response

方法

方法描述
status(code: number): Response设定状态码,可以链式调用
send(html: string)返回HTML代码
sendFile(filePath: string)返回文件(文件路径要绝对路径)
json(data: Object)返回Json对象
render(view: string, options: object|undefined, callback?: ((err: Error, html: string) => void))渲染并返回HTML代码(示例见:HandleBars
header(name: string, value: string |number |string[]): Response设置响应头
setHeader(name: string, value: string |number |string[]): Response设置响应头
getHeader(name: string): string | number | string[] | undefined获取响应头
getHeaders(): Object获取所有响应头
cookie(name: string, val: string, options: CookieOptions)设置cookie

CookieOptions 选项:

CookieOptions {
    domain: string;
    path: string; // 默认 "/"
    maxAge: number; // 毫秒
    expires: Date;
    secure: boolean;
    httpOnly: boolean;
    sameSite: "strict"|"lax"|"none";
}
Last Updated 2024/4/10 18:49:04