目录
- 正文
- 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 来保存接口重复发送测试接口。