目录
- 正文
- IncomingMessage
- ServerResponse
- 请求对象 req
- 响应对象
- 设置状态码
- 如何来快速测试这些属性和方法呢?
- 下面给出一些示例代码
- 目录结构
- 安装依赖
- 小结
正文
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()
console.log(res.statusCode)
res.send("get v: 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>
<h>欢迎来到 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": ".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": "^.21.0",
"@babel/plugin-syntax-jsx": "^.18.6",
"@babel/preset-env": "^.20.2",
"@babel/preset-react": "^.18.6",
"babel-jest": "^.4.3",
"dirname-filename-esm": "^.1.1",
"ejs": "^.1.8",
"express": "^.18.2",
"jest": "^.4.3",
"nodemon": "^.0.20",
"supertest": "^.3.3"
}
}
看看 babel 配置
export default {
presets: [
["@babel/preset-env", { targets: { node: "current" } }],
"@babel/preset-react",
],
};
看看 eslint 配置
module.exports = {
"env": {
"browser": true,
"es": 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); //::
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 a = req.get("set-cookie");
console.log("set-cookie", a); // undefined
//------------- header ------------- //
let a = req.header("set-cookie");
console.log("set-cookie", a); // undefined
//------------- accepts ------------- //
let b = req.accepts();
console.log("accepts", b);
// accepts [
// 'image/avif',
// 'image/webp',
// 'image/apng',
// 'image/svg+xml',
// 'image/*',
// '*/*'
// ]
//------------- acceptsEncodings ------------- //
let b = req.acceptsEncodings();
console.log("acceptsEncodings", b); // [ 'gzip', 'deflate', 'br', 'identity' ]
//------------- acceptsLanguages ------------- //
let c = req.acceptsLanguages();
console.log("acceptsLanguages", c); // [ 'zh-CN', 'zh' ]
//------------- range ------------- //
let range = req.range(, {});
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();
console.log(res.statusCode);
res.send("get v: hello world! and status code: 203 === " + res.statusCode);
});
app.get("/res/statusCode", (req, res, next) => {
res.send("get v: hello world! and status code:" + res.statusCode);
});
app.get("/res/links", (req, res, next) => {
res.links({
a: "http://localhost:",
});
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: "" }); // 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();
});
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></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().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", " Warning");
console.log(res.get("Warning")); // Warning Warning
res.send("append");
});
app.get("/res/send/set", (req, res, next) => {
res.set("set", "set8888"); //响应 header 中
res.send("set");
});
app.get("/res/send/header", (req, res, next) => {
res.header("set", "set9999"); //响应 header 中
res.send("set");
});
app.get("/res/send/get", (req, res, next) => {
res.set({
"Content-Type": "text/plain",
"Content-Length": "",
ETag: "",
});
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(, () => {
console.log("listening on http://localhost:");
});
小结
本文主要介绍了 express 的请求和响应对象,以及集成对象,各种用途,并使用一个实例,来进行说明。在测试实际接口时候,使用了 nodemon 来自动重启服务,使用 apifox 来保存接口重复发送测试接口。