当谈到用Node.js构建网络应用时,Express.js是最受欢迎的选择。然而,当用Node.js说网络应用时,它往往不是为了在浏览器中可见的东西(不包括前端应用的服务器端渲染)。相反,Express.js是Node.js的一个网络应用框架,使你能够在Node.js中构建服务器应用程序。作为一个后端应用程序,它是你的前端应用程序和潜在的数据库或其他数据源(如REST APIs,GraphQL APIs)之间的胶水。只是为了给你一个概念,以下是构建客户端-服务器架构的技术堆栈列表。
- React.js(前端)+ Express.js(后端)+ PostgreSQL(数据库)
- Vue.js(前端)+Koa.js(后端)+MongoDB(数据库)
- Angular.js(前端)+ Hapi.js(后端)+ Neo4j(数据库)
Express.js在后端可以与其他网络应用框架交换,就像React.js在前端应用时可以与Vue.js和Angular.js交换一样。Node.js生态系统并不只提供一种解决方案,而是提供了各种解决方案,它们都有各自的优势和劣势。然而,对于这个应用程序,我们将使用Express服务器,因为它是用Node.js构建JavaScript后端应用程序时最流行的选择。
之前的Node.js应用程序带有一个观察者脚本,用于在你的源代码发生变化后重新启动你的应用程序,Babel用于启用Node.js中还不支持的JavaScript功能,以及用于你的应用程序的敏感信息的环境变量。这是一个很好的基础,可以让你在Node.js中开始使用Express.js。让我们继续在你的Node.js应用程序中安装Express.js,从之前的命令行开始。
npm install express
现在,在你的src/index.jsJavaScript 文件中,使用以下代码导入 Express.js,创建一个 Express 应用程序的实例,并将其作为 Express 服务器启动。
import express from 'express';
const app = express();
app.listen(3000, () =>
console.log('Example app listening on port 3000!'),
);
一旦你在命令行中用npm start ,启动你的应用程序,你应该能在命令行中看到输出。
Example app listening on port 3000!
你的Express服务器已经启动并运行。在你的Express应用程序启动之后,所有应该发生的事情都进入了回调函数。该方法本身需要另一个参数作为第一参数,即运行中的应用程序的端口。这就是为什么在启动之后,应用程序可以在浏览器中通过http://localhost:3000 ,尽管当你在浏览器中访问它时,这个URL上还没有任何东西可用。
Express.js中的路由
后台的Web应用程序中的路由是用来将URI映射到中间件的。这些URI可以通过REST或GraphQL提供文本信息、HTML页面或JSON数据。在一个较大的应用程序中,这意味着有几个路由(中间件)映射到几个URI。在Express中,一个中间件就是路由所需要的一切,因为路由只是上面的另一个抽象概念。让我们用Express建立这样一个单一的路由:
import express from 'express';
const app = express();
app.get('/', (req, res) => {
res.send('Hello World!');
});
app.listen(3000, () =>
console.log('Example app listening on port 3000!'),
);
这个路由指向你的域名的根(/)。在浏览器中,你可以用http://localhost:3000/ 或http://localhost:3000 来访问这个路由,而不用尾部的斜线。一旦你保存了这个文件,由于我们的设置,应用程序应该自动重新启动。你可以在命令行中验证。之后,访问浏览器,看看它为你输出了什么。你应该在那里看到打印的 "Hello World!"。在我们的代码中,我们正在使用res 对象的send 方法来向我们的客户端应用程序发送一些东西。res 对象是我们为客户指定响应的所有内容,而req 对象是我们从客户的这个特定请求中得到的所有内容。这就是你在Express.js中的第一个路由了。我们将在后面学习更多关于路由的知识,以及如何与它们互动。
从本质上讲,每个Express应用程序都是一系列路由和中间件函数的调用。你已经看到了前者,即带有单一路由的路由,以前是http://localhost:3000 URL或/ 路由。你可以通过在Express.js中使用路由(例如:/example )来扩展应用程序,使其具有更多的URI(例如:http://localhost:3000/example ),如前所示。自己试试吧!
Express.js中的中间件
如果一个Express应用程序由前面提到的路由和中间件功能调用组成,那么中间件的功能调用又是什么呢?在Express.js中,有两种中间件:应用级中间件和路由器级中间件。让我们在本节中通过一个整洁的用例来探索一个应用级的中间件,并在后面深入探讨应用级和路由器级中间件的其他方面。
在使用Express.js时,人们在访问他们的Express应用程序时经常在浏览器中遇到以下错误。
"Cross-Origin Request Blocked: The Same Origin Policy disallows reading the remote resource at http://localhost:3000/. (Reason: CORS header ‘Access-Control-Allow-Origin’ missing)."
这很可能是因为我们从一个外国的域中访问一个域而发生的。跨源资源共享(CORS)的发明是为了保证网络应用在域一级的安全。其理念是:不应该从其他域中访问数据。例如,一个域名为https://example.com 的网络应用程序不应该被允许访问另一个默认为https://website.com 的网络应用程序。CORS被用来限制网络应用程序之间的访问。
现在,我们可以通过添加这个缺失的 CORS 标头来允许 CORS,因为在为我们的 Express 服务器实现一个消费客户端应用程序时,我们自己最终会遇到这个错误。然而,由于我们不想为每条路线手动做这件事,我们可以使用一个应用程序级的中间件来默认为每个请求添加 CORS HTTP 头。因此,我们可以自己编写一个中间件 -- 我们将在后面看到这个工作 -- 或者使用一个现成的Express.js中间件库,它正在为我们做这项工作。
npm install cors
接下来通过向Express实例的use 方法提供它,将其作为整个应用程序的中间件。
import 'dotenv/config';
import cors from 'cors';
import express from 'express';
const app = express();
app.use(cors());
app.get('/', (req, res) => {
res.send('Hello World!');
});
app.listen(3000, () =>
console.log(`Example app listening on port 3000!`),
);
Express应用程序可以使用一个来自外部库或自己构建的中间件,来扩展它的所有路由(应用级中间件)。在这种情况下,所有路由都被扩展了 CORS HTTP 头信息。默认情况下,现在所有的路由都可以被所有的域访问。这也包括后来我们的开发域从我们的消费客户端应用程序。毕竟,这只是对Express中间件的一个偷窥。我们将在后面学习更多关于应用级和路由器级的中间件,以及如何自己写一个中间件。
注意:如果你还没有完全掌握 CORS 配置的目的,就不要担心。这是许多第一次使用Express的人遇到的事情之一,他们不得不通过安装这个整洁的库来处理,而且往往从不回头看他们为什么要安装和使用它。如果你还不了解它,不用担心,但在你将你的应用程序部署到生产中时,你应该设置一个允许访问你的 Express 服务器应用程序的域的白名单。CORS库提供了这样的配置。花点时间自己研究一下吧。
Express.js中的环境变量
在你为你的Node.js应用程序设置了环境变量之前。让我们用一个环境变量来设置你的端口,而不是在源代码中硬编码。如果没有这样的文件,在你的项目中创建一个新的*.env文件。否则就使用已经存在的.env*文件。给它一个新的键值对来定义你的端口。
PORT=3000
现在在你的src/index.js文件中,导入 node 包,使环境变量在你的源代码中可用,并使用PORT 环境变量来启动你的 Express 应用程序。
import 'dotenv/config';import cors from 'cors';import express from 'express';
const app = express();
app.use(cors());
app.get('/', (req, res) => { res.send('Hello World!');});
app.listen(process.env.PORT, () => console.log(`Example app listening on port ${process.env.PORT}!`),);
你没有暴露在源代码中使用的端口,而是把它储存在环境变量中一个更敏感的地方。如果你使用GitHub之类的Git,你可以把*.env添加到你的.gitignore文件中,排除它被上传到GitHub仓库的可能性。这就是敏感数据远离GitHub等公共仓库的方法。如果你最终将你的应用程序部署到生产环境中,你可以将环境变量作为.env*文件添加到为你的应用程序服务的 Web 服务器上。