Express 恳求 req 和呼应 res 目标定义:

var req = Object.create(http.IncomingMessage.prototype)
var res = Object.create(http.ServerResponse.prototype)

下面是特点承继联系:

原型 承继来历类
http.IncomingMessage.prototype Stream.Reabable
http.ServerResponse.prototype IncomingMessage

IncomingMessage

 class IncomingMessage extends stream.Readable {
    constructor(socket: Socket);
    aborted: boolean;
    httpVersion: string;
    httpVersionMajor: number;
    httpVersionMinor: number;
    complete: boolean;
    connection: Socket;
    socket: Socket;
    headers: IncomingHttpHeaders;
    rawHeaders: string[];
    trailers: NodeJS.Dict<string>;
    rawTrailers: string[];
    setTimeout(msecs: number, callback?: () => void): this;
    method?: string | undefined;
    url?: string | undefined;
    statusCode?: number | undefined;
    statusMessage?: string | undefined;
    destroy(error?: Error): this;
}

ServerResponse

class ServerResponse<Request extends IncomingMessage = IncomingMessage> extends OutgoingMessage<Request> {
    statusCode: number;
    statusMessage: string;
    constructor(req: Request);
    assignSocket(socket: Socket): void;
    detachSocket(socket: Socket): void;
    writeContinue(callback?: () => void): void;
    writeEarlyHints(hints: Record<string, string | string[]>, callback?: () => void): void;
    writeHead(
        statusCode: number,
        statusMessage?: string,
        headers?: OutgoingHttpHeaders | OutgoingHttpHeader[],
    ): this;
    writeHead(statusCode: number, headers?: OutgoingHttpHeaders | OutgoingHttpHeader[]): this;
    writeProcessing(): void;
}

接下来的任务仍是很简单,看看 express 是怎么处理恳求 req 目标上的特点和办法。

恳求目标 req

在 req 目标上扩展办法

特点和办法名 阐明
get()/header() 回来指定的 HTTP 恳求头字段(不区别大小写的匹配)。
accepts() 根据恳求的 HTTP 标字段查看指定的内容类型是否可承受。
acceptsEncodings() 回来指定编码的第一个承受编码。
acceptsCharsets() 回来指定字符集的第一个承受的字符集。
acceptsLanguages() 回来指定言语的第一个承受言语。
range() Range 标头解析器。
param() 回来 req 目标中 params
is() 假如传入恳求的 内容类型 HTTP 头字段,则回来匹配的内容类型 匹配参数指定的 MIME 类型。

运用 defineGetter 函数扩展特点:

function defineGetter(obj, name, getter) {
  Object.defineProperty(obj, name, {
    configurable: true,
    enumerable: true,
    get: getter
  });
}
特点 阐明
protocol 协议
secure 是否安全
ip 恳求的 ip 地址
ips 恳求头中的 ip 地址数组
subdomains 恳求中的子域名
path 包括恳求 URL 的途径部分。
hostname 主机名
fresh 是否为最新的
stale 是否为过时的
xhr 恳求中是否包 xmlHTTPRequest 字符串

这是特点仍是跟 HTTP 通讯,前后端通讯 xhr,如:完好的途径 path/protocol/secure/subdomains, ip 相关,服务器相关 fresh/stable。

呼应目标

在 res 目标上扩展办法:

特点和办法名 阐明
status() 设置呼应状况码。
links() 用给定的 links 设置头字段
send() 发送 HTTP 呼应。
json() 发送 JSON 呼应。
jsonp() 发送 JSONP 呼应。
sendStatus() 发送状况码
sendFile() 在给定的途径处传输文件。
sendfile() 在给定的 .设置呼应 HTTP 头字段 基于文件名的扩展名。
download() 下载文件
type() 将 HTTP 标头设置为由指定的。
format() 格式化恳求目标的上内容
attachment() 在呼应头中添加额定的内容
append() 将数据最加到尾部
set()/header() 设置 http 头信息
get() 获取指定 http 头数据
clearCookie() 清除 cookie 内容
cookie() 设置 cookie
location() 将呼应 HTTP 标头设置为指定的参数。
redirect() 重定向地址
vary() 运用 vary 办法添加字段到恳求头
render() 烘托模板中 html

设置状况码

res.status(203)
console.log(res.statusCode)
res.send("get v1: hello world!")

怎么来快速测验这些特点和办法呢?

  • 准备好接口, 了解 restful api 或者其他范式的形式接口
  • 准备写接口时的东西。curl(了解命令行)、东西(类似于:postman 等等)
  • 将东西接口与 express 的接口对应起来进行调试测验,验证特点。本项目运用

下面给出一些示例代码

目录结构

.
├── __tests__
├── babel.config.js
├── index.js
├── index.md
├── jest.config.js
├── node_modules
├── package.json
├── pnpm-lock.yaml
├── public
└── views

安装依靠

  • views 中的 home.ejs 需求 ejs, 内容如下:
<html>
<head>
    <title>Home 页面</title>
</head>
<body>
    <h2>欢迎来到 Home 页面</h2>
</body>
</html>

安装其他的依靠包:

pnpm install ejs babel-jest dirname-filename-esm jest nodemon supertest @babel/preset-react @babel/preset-env @babel/plugin-syntax-jsx @babel/core

看看 package.json 项目装备

{
  "name": "debugger-source-code",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "type": "module",
  "scripts": {
    "dev": "nodemon index.js",
    "test": "NODE_OPTIONS=--experimental-vm-modules jest"
  },
  "keywords": [],
  "author": "",
  "license": "ISC",
  "dependencies": {
    "@babel/core": "^7.21.0",
    "@babel/plugin-syntax-jsx": "^7.18.6",
    "@babel/preset-env": "^7.20.2",
    "@babel/preset-react": "^7.18.6",
    "babel-jest": "^29.4.3",
    "dirname-filename-esm": "^1.1.1",
    "ejs": "^3.1.8",
    "express": "^4.18.2",
    "jest": "^29.4.3",
    "nodemon": "^2.0.20",
    "supertest": "^6.3.3"
  }
}

看看 babel 装备

export default {
  presets: [
    ["@babel/preset-env", { targets: { node: "current" } }],
    "@babel/preset-react",
  ],
};

看看 eslint 装备

module.exports = {
    "env": {
        "browser": true,
        "es2021": true
    },
    "extends": "eslint:recommended",
    "overrides": [
    ],
    "parserOptions": {
        "ecmaVersion": "latest",
        "sourceType": "module"
    },
    "rules": {
    }
}

看看 jest 装备

export default {
  transform: {
    '\\.[jt]s?$': 'babel-jest'
  },
};

express 主要服务 index.js

import express from "express";
import path from "path";
import { dirname } from "dirname-filename-esm";
const __dirname = dirname(import.meta);
const app = express();
app.set("view engine", "ejs");
app.use(express.static(path.join(__dirname, "public")));
app.get("/req", (req, res, next) => {
  console.log(req.protocol); // http 协议
  console.log(req.secure); //fals
  console.log(req.ip); //::1
  console.log(req.ips); // []
  console.log(req.subdomains); // []
  console.log(req.path); // /favicon.ico
  console.log(req.host); // localhost 已经被抛弃
  console.log(req.hostname); // localhost
  console.log(req.fresh); // false
  console.log(req.stale); // true
  console.log(req.xhr); //false
  //------------- get ------------- //
  let a1 = req.get("set-cookie");
  console.log("set-cookie", a1); // undefined
  //------------- header ------------- //
  let a2 = req.header("set-cookie");
  console.log("set-cookie", a2); // undefined
  //------------- accepts ------------- //
  let b1 = req.accepts();
  console.log("accepts", b1);
  //   accepts [
  //   'image/avif',
  //   'image/webp',
  //   'image/apng',
  //   'image/svg+xml',
  //   'image/*',
  //   '*/*'
  // ]
  //------------- acceptsEncodings ------------- //
  let b2 = req.acceptsEncodings();
  console.log("acceptsEncodings", b2); //  [ 'gzip', 'deflate', 'br', 'identity' ]
  //------------- acceptsLanguages ------------- //
  let c1 = req.acceptsLanguages();
  console.log("acceptsLanguages", c1); // [ 'zh-CN', 'zh' ]
  //------------- range ------------- //
  let range = req.range(10, {});
  console.log("range", range); // undefined
  //------------- param ------------- //
  let param = req.param();
  console.log("param", param); // undefined
  res.send("hello world!");
});
app.get("/res/status", (req, res, next) => {
  res.status(203);
  console.log(res.statusCode);
  res.send("get v1: hello world! and status code: 203 === " + res.statusCode);
});
app.get("/res/statusCode", (req, res, next) => {
  res.send("get v1: hello world! and status code:" + res.statusCode);
});
app.get("/res/links", (req, res, next) => {
  res.links({
    a: "http://localhost:3232",
  });
  res.send("links set"); // header Link filed
});
app.get("/res/send", (req, res, next) => {
  res.send("links set"); //type: string
});
app.get("/res/send/object", (req, res, next) => {
  res.send({ msg: "123" }); // type object json
});
app.get("/res/send/json", (req, res, next) => {
  res.json(JSON.stringify({ msg: "json" })); // type object json
});
app.get("/res/send/jsonp", (req, res, next) => {
  let fn = req.query.fn;
  let data = JSON.stringify({
    data: "mydata",
  });
  res.end(fn + data); // type object json
});
app.get("/res/send/sendStatus", (req, res, next) => {
  res.sendStatus(404);
});
app.get("/res/send/sendFile", (req, res, next) => {
  res.sendFile(path.join(__dirname, "jest.config.js"));
});
app.get("/res/send/download", (req, res, next) => {
  res.download(path.join(__dirname, "jest.config.js"));
});
app.get("/res/send/type", (req, res, next) => {
  res.type(".html").send("<div>123</div>");
  // image/png
  console.log(res.get("Content-type"));
});
app.get("/res/send/format", (req, res, next) => {
  res.format({
    "text/html": function () {
      res.send("<div>This is html</div>");
    },
    "text/pain": function () {
      res.send("this is html text");
    },
    "application/json": function () {
      res.send({ message: "This is html json" });
    },
    default: function () {
      res.status(406).send("Not Acceptable");
    },
  });
});
app.get("/res/send/attachment", (req, res, next) => {
  res.attachment("index.md");
  console.log(req.get("Content-Disposition"));
  res.send("attachment");
  // 	attachment; filename="index.md"
});
app.get("/res/send/append", (req, res, next) => {
  res.append("Warning", "201 Warning");
  console.log(res.get("Warning")); // Warning	201 Warning
  res.send("append");
});
app.get("/res/send/set", (req, res, next) => {
  res.set("set8", "set8888"); //呼应 header 中
  res.send("set");
});
app.get("/res/send/header", (req, res, next) => {
  res.header("set9", "set9999"); //呼应 header 中
  res.send("set9");
});
app.get("/res/send/get", (req, res, next) => {
  res.set({
    "Content-Type": "text/plain",
    "Content-Length": "123",
    ETag: "12345",
  });
  let ct = res.get("Content-Type"); //呼应 header 中
  res.send("[get => ]" + ct);
});
app.get("/res/send/cookie", (req, res, next) => {
  res.cookie("abc", "dd"); //呼应 header 中
  res.send("cookie: abcdd");
});
app.get("/res/send/clearCookie", (req, res, next) => {
  res.cookie("abc", "dd");
  res.cookie("def", "xj");
  res.clearCookie("abc");
  res.send("cookie: abcdd");
});
app.get("/res/send/location", (req, res, next) => {
  res.location("http://demo.com");
  console.log(res.get("location")); // http://demo.com
  res.send(res.get("location"));
});
app.get("/res/send/redirect", (req, res, next) => {
  res.redirect("/res/send/redirect-new");
});
app.get("/res/send/redirect-new", (req, res, next) => {
  res.send("this is redirect-new");
});
app.get("/res/send/vary", (req, res, next) => {
  res.vary("User-Agent").send("Field added to the Vary response header");
});
app.get("/res/send/render", (req, res, next) => {
  res.render('home')
});
app.listen(3232, () => {
  console.log("listening on http://localhost:3232");
});

小结

本文主要介绍了 express 的恳求和呼应目标,以及集成目标,各种用处,并运用一个实例,来进行阐明。在测验实际接口时候,运用了 nodemon 来自动重启服务,运用 apifox 来保存接口重复发送测验接口。