Express
是一个Node.js
的web
框架,Node.js
是一个构建网络服务和应用很棒的工具.Express
是在其基础上 api 上,提供易于使用的功能,来满足web
服务器的使用需求.
它是开源且免费的,易于扩展,性能优良的,并且有很多预制包可供你直接使用,来执行各种事情.
你可以使用pnpm
来安装express
到各种项目中
pnpm i express --save
先创建一个空白目录,CD
到目录中,使用pnpm init - y
来创建 package.json
文件
我们先来创建一个最简单的hello world
的 web 服务器吧
这是以下代码:
const express = require('express')
cosnt app = express()
app.get('/', (req,res) => {
res.send('Hello World')
})
app.listen(3000,() => console.log('服务运行在localhost:3000'))
将上述代码保存为 app.js
文件到你项目根路径中,并且开启服务
node app.js
此时打开浏览器到: localhost:3000
你会看到 Hello World
的信息.
上面四行代码在屏幕背后做了很多,
首先,我们导入了 express
的包到 express
值中,我们通过调用应用程序的 app()
方法来实例化一个应用程序.
一但有了应用对象,我们通过使用 app.get()
来告诉它在 /
路径上监听 get
请求.
每一个http
动词都有一个方法: get()
,post()
,put()
,delete()
,path()
:
app.get("/", (req, res) => {
/* */
});
app.post("/", (req, res) => {
/* */
});
app.put("/", (req, res) => {
/* */
});
app.delete("/", (req, res) => {
/* */
});
app.path("/", (req, res) => {
/* */
});
当一个请求开始被调用的时候,我们通过使用一个回调函数来处理它们;
我们使用一个箭头函数:
(req, res) => res.send("Hello World");
Express
在这个回调函数中给我们提供了两个对象,分别是 req
和 res
,我们称为 "请求对象" 和 "响应对象"
Request
是 http
请求,它可以提供给我们所有的信息,包括: 请求参数,请求头,请求体等等....
Response
是我们发给客户端的响应对象,我们在这个回调中所作的就是使用 Respone.send()
方法将 Hello world
字符串发送给客户端
这个方法将此字符串设置为正文,并且关闭了链接
第四行代码实际上启动了服务器,并且告诉它监听 3000
端口上的服务.
一份关于各种请求对象属性以及使用说明:
前面我提到了 Request
对象如果保存所有的 HTTP 请求信息.
一下属性你可能会经常用到:
Property | Description |
---|---|
.app | 包含对 Express 应用对象的引用 |
.baseUrl | 每一个应用相应的基础路径 |
.body | 包含在请求正文中所提交的数据(必须在访问前手动解析和填充) |
.cookies | 包含请求所返送的 cookies(需要 cookie-parser 中间件) |
.hostname | 服务器主机名 |
.ip | 服务器 ip |
.method | 使用的 HTTP 方法 |
.params | 路由命名的参数 |
.path | URL 路径 |
.protocol | 请求协议 |
.query | 一个包含请求中使用的所有查询字符串的对象 |
.secure | 如果请求时安全的(使用 HTTPS) |
.signedCookies | 包含由请求发送的已签名的 cookies (需要cookie-parser 中间件) |
.xhr | 如果请求是XMLHttpRequest 为 true |
查询字符串是在 URL 路径之后的部分,以问好"?"开头.
例如:
?name=Jack
使用 &
来添加多个查询参数
?name=Jack&age=35
如何在 Express 中获取这些查询字符串的值?
Express 通过为我们提供了 Request.query
对象使其获取变得非常简单
const express = require("express");
const app = express();
app.get("/", (req, res) => {
console.log(req.query);
});
app.listen(8000);
这个对象为每一个查询参数填充一个属性.
如果没有查询参数,它就是一个空对象
这样的话就可以很容易的使用 for...in
循环对它进行迭代.
for (const key in req.query) {
console.log(key, req, query[key]);
}
然后就会打印出每一个查询参数的键和值
你可以这样访问每一个单独的属性;
req.query.name; // Jack
req.query.age; // 35
post 查询参数是由http
客户端发送的,例如通过表单,或在执行发送数据的 post 请求发送数据.
那么我们如何访问这些数据了?
如果发送的是 JSON 数据, 使用 conten-type : application/json
,你将会使用 express.json()
这个中间件:
const express = require("express");
const app = express();
app.use(express.json());
如果数据作为 JSON 发送, 使用 Content-Type: application/x-www-form-urlencoded
你将会使用express-urlencoded()
中间件:
const express = require("express");
const app = express();
app.use(express.urlencoded());
在这两种情况下,你都可以通过引用 Request.body
中的数据来访问他:
app.post("/form", (req, res) => {
const name = req.body.name;
});
注意: 旧版的 Express 需要使用 body-parser 模块来处理 post 数据,从 Express 4.16(2017 年 9 月发布)开始,这种情况就不存在了
中间件是一个在路由处理过程中的勾子函数,在某些时刻执行一些操作,这取决于你想让它做什么.
它通常用来编辑请求或相应对象,或在请求到达路由处理程序之前终止请求.
它像这样被添加到执行堆栈中:
app.use((req, res, next) => {
/* */
});
我们通常要在中间件函数的末尾调用 next()
,用来传递给下一个处理程序,除非我们像过早的结束响应.并把它发送给客户端.
我们通常使用npm
包中预制好的中间件,这是一个中间件列表
一个例子是 cookie-parser
,它被用来解析 cookie
到req.cookie
对象,使用 npm install-parser
来安装它,你可以这样使用:
const express = require("express");
const app = express();
const cookieParser = require("cookie-parser");
app.get("/", (req, res) => res.send("Hello World!"));
app.use(cookieParser());
app.listen(3000, () => console.log("Server ready"));
你也可以设置一个中间件函数只为特定的路由运行,而不是所有路径运行,方法是把它作为路由定义的第二个参数:
const myMiddleWare = (req, res, next) => {
/* */
next();
};
app.get("/", myMiddleWare, (req, res) => res.send("Hello World!"));
如果你需要存储一个中间件中生成的数据,以便将其传递给后续的中间件函数或请求处理程序.你可以使用 Request.local
对象.它将该数据添加到当前请求中.
req.locals.name = "Jack";
如何在 Express 中从文件夹中提供静态文件资源了?
我们一般在一个单独的 public
文件夹中放置图片,CSS 文件,并且把他们暴露在根路径下.
const express = require("express");
const app = express();
app.use(express.static(__dirname + "/public"));
/* ...*/
app.listen(3000, () => console.log("Server ready"));
如果你有一个 index.html
文件在 /public
中,点击 http://localhost:3000
这将地址你会看到对应的文件
这个 __dirname
变量是一个字符串,其中包含了项目根目录的绝对路径,必须要和包含静态文件的路径相连接配合
Express 提供了一个简单的方法来传输文件作为附件, Response.download()
一旦用户点击了使用该方法发送文件的路由,浏览器将会提示用户下载文件.
Response.download()
方法允许你去发送一个附加在请求中的文件.并且浏览器不会在页面中显示该文件,而是将其保存到磁盘中
app.get("/", (req, res) => res.download(__dirname + "./file.pdf"));
应用程序:
const express = require("express");
const app = express();
app.get("/", (req, res) => res.download(__dirname + "./file.pdf"));
这个方法也提供了一个回调函数,一但文件被发送后,你可以用这个回调函数来执行对应的代码:
res.download("./file.pdf", "user-facing-filename.pdf", (err) => {
if (err) {
// handle error
return;
} else {
// do something
}
});
如果使用 express 处理表单?
下面是一个 HTML
表单的例子:
<form method="POST" action="/submit-form">
<input type="text" name="username" />
<input type="submit" />
</form>
当用户按下提交按钮,浏览器将会自动发送一个 post
请求到与页面同源的 /submit-form
链接中,发送其中的数据.
当编码为 application/x-www-form-urlencoded
时,表单数据包含了用户输入字段的值.
表单也可以使用 get
方法发送数据,但是绝大多数的表单都是使用 post
方法
表单数据将会被放置在 post
请求体中正文中
为了解析post
请求,你将会使用 Express 提供的 express.urlencoded()
中间件
const express = require("express");
const app = express();
app.use(express.urlencoded());
现在你需要在 /submit-form
路由上创建一个 Post 端口,任何数据都将可以在 Request.body
上获得.
app.post("/submit-form", (req, res) => {
const username = req.body.username;
// ...
res.send();
});
不要忘记在使用数据之前用 express-validator
来验证它.
如何在 Express 中管理存储并且处理通过表单上传的文件
这是一个允许用户上传文件的form
表单的html
例子:
<form method="POST" action="/submit-form">
<input type="file" name="document" />
<input type="submit" />
</form>
当用户点击了提交按钮,浏览器会自动发送一个 post
请求到与页面同源 的/submit-form
地址,发送其中包含的数据,不是编码成 application/x-www-form-urlencoded
的正常形式,而是作为 multipart/form-data
.
在服务器端,处理各种各样杂乱的数据会很麻烦,而且会容易出错,所以我们需要使用一个实用库叫做: formidable
这是github链接
你可以这样来安装使用:
pnpm i formidable
接着,在你的 Node.js 文件中添加它:
const express = require("express");
const app = express();
const formidable = require("formidable");
现在在 /submit-form
路线的 post
端点,我们使用formidable.Incoming From()
实例化了一个新的`for 秒不了表单:
app.post("/submit-form", (req, res) => {
new formidable.IncomingFrom();
});
做完了以上的配置,我们需要解析这个表单,我们可以通过提供一个回调来做异步操作,这代表着所有的文件都会被处理,一但formidable
完成,它就会让他们可用.
app.post("/submit-form", (req, res) => {
new formidable.IncomingFrom().parse(req, (err, fields, files) => {
if (err) {
console.error("Error", err);
throw err;
}
console.log("Fields", fields);
console.log("Files", files);
files.map((file) => {
console.log(file);
});
});
});
或者你可以使用事件而不是回调,在每一个文件被解析时候得到通知,以及其他的事件,比如结束处理,收到一个非文件字段,或者发生错误.
app.post("/submit-form", (req, res) => {
new formidable.IncomingFrom()
.parse(req)
.on("field", (name, field) => {
console.log("Field", name, field);
})
.on("file", (name, file) => {
console.log("Uploaded file", name, file);
})
.on("aborted", () => {
console.error("Request aborted by the user");
})
.on("error", (err) => {
console.error("Error", err);
throw err;
})
.on("end", () => {
res.end();
});
});
不论你选择什么方法,你都会得到一个或多个Formidable.File
对象,它给你提供了有关上传文件的信息,以下是你可以调用的一些方法.
file.size
, 以字节为单位的文件大小file.path
,被写入的文件的路径file.name
,文件名file.type
,文件类型该路径默认为来临时文件夹,如果你监听了 fileBegin
事件时可以修改.
app.post("/submit-form", (req, res) => {
new formidable.IncomingFrom()
.parse(req)
.on("fileBegin", (name, file) => {
form.on("fileBegin", (name, file) => {
file.path = __dirname + "/uploads/" + file.name;
});
})
.on("file", (name, file) => {
console.log("Uploaded file", name, file);
});
//...
});