Node.js:ExpressWeb
Express/Node 入门
### 什么是 Node? Node(正式名称 Node.js)是一个开源的、跨平台的运行时环境,有了它,开发人员可以使用 JavaScript 创建各种服务器端工具和应用程序。此运行时主要用于浏览器上下文之外(即可以直接运行于计算机或服务器操作系统上)。据此,该环境省略了一些浏览器专用的 JavaScript API,同时添加了对更传统的 OS API(比如 HTTP 库和文件系统库)的支持。 ### Web 框架 Node 本身并不支持其他常见的 web 开发任务。如果需要进行一些具体的处理,比如运行其他 HTTP 动词(比如 `GET`、`POST`、`DELETE` 等)、分别处理不同 URL 路径的请求(“路由”)、托管静态文件,或用模板来动态创建响应,那么可能就要自己编写代码了,亦或使用 web 框架,以避免重新发明轮子。 ### 什么是 Express? Express 是最流行的 Node 框架,是许多其他流行 Node 框架 的底层库。它提供了以下机制: - 为不同 URL 路径中使用不同 HTTP 动词的请求(路由)编写处理程序。 - 集成了“视图”渲染引擎,以便通过将数据插入模板来生成响应。 - 设置常见 web 应用设置,比如用于连接的端口,以及渲染响应模板的位置。 - 在请求处理管道的任何位置添加额外的请求处理“中间件”。 虽然 Express 本身是极简风格的,但是开发人员通过创建各类兼容的中间件包解决了几乎所有的 web 开发问题。这些库可以实现 cookie、会话、用户登录、URL 参数、`POST` 数据、安全头等功能。可在 Express 中间件 网页中找到由 Express 团队维护的中间件软件包列表(还有一张流行的第三方软件包列表)。 ### Helloworld Express ```js ////通过 require() 导入 Express 模块 const express = require("express"); //创建了一个 Express 应用,命名为 app。它可以进行路由 HTTP 请求、配置中间件、渲染 HTML 视图、注册模板引擎以及修改 应用程序设置 等操作,从而控制应用的行为(例如,环境模式,路由定义是否为区分大小写等)。 const app = express(); //路由定义。app.get() 方法指定了一个回调函数,该函数在每监听到一个关于站点根目录路径('/')的 HTTP GET 请求时调用。此回调函数以一个请求和一个响应对象作为参数,并直接调用响应的 send() 来返回字符串“Hello World!” app.get("/", (req, res) => { res.send("Hello World!"); }); //在“3000”端口上启动服务器,并在控制台打印日志。 app.listen(3000, () => { console.log("示例应用正在监听 3000 端口 !"); }); ``` ### 导入和创建模块 模块是 JavaScript 库或文件,可以用 Node 的 `require()` 函数将它们导入其他代码。Express 本身就是一个模块,Express 应用中使用的中间件和数据库也是。 ```js //调用 require() 函数,用字符串('express')指定模块的名字,然后调用返回的对象来创建 Express 应用。然后就可以访问应用对象的属性和函数了。 const express = require("express"); const app = express(); ``` ##### 自定义模块 ```js //square.js 模块 exports.area = (width) => { return width * width; }; exports.perimeter = (width) => { return 4 * width; }; ``` ```js //用 require() 导入这个模块,然后调用导出的方法 //这里 require() 了文件名,省略了 .js 扩展名(可选) const square = require("./square"); console.log("边长为 4 的正方形面积为 " + square.area(4)); ``` ```js //一次赋值不仅能构建一个单一的属性,还能构建一个完整的对象,可以像下面这样把对象赋值给 module.exports module.exports = { area: (width) => { return width * width; }, perimeter: (width) => { return 4 * width; }, }; ``` ### 使用异步 API 在 Node 中使用无阻塞异步 API 甚至比在浏览器中更为重要,这是因为 Node 是一个单线程事件驱动的执行环境。“单线程”意味着对服务器的所有请求运行在同一个线程上,而不是分布在不同的进程上。这个模式在速度和管理服务器资源方面效率很高,但也意味着如果以同步方式调用的函数占用了很长时间,不仅会阻塞当前请求,还会阻塞当前 web 应用其他所有请求。 ```js setTimeout(() => { console.log("第一"); }, 3000); console.log("第二"); ``` 有多种方法可以让一个异步 API 通知当前应用它已执行完毕。最常用的是在调用异步 API 时注册一个回调函数,在 API 操作结束后将“回调”之。这也是上面的代码所使用的方法。 > **备注:** 如果有一系列独立的异步操作必须按顺序执行,那么使用回调可能会非常“混乱”,因为这会导致多级嵌套回调。人们通常把这个问题叫做“回调地狱”。缓解这个问题有以下办法:良好的编码实践、使用 async 等模块、迁移至 ES6 并使用 Promise 等特性。 > **备注:** Node 和 Express 有一个一般性约定,即:使用“错误优先”回调。这个约定要求回调函数的第一个参数是错误值,而后续的参数包含成功数据。 ### 使用中间件 中间件在 Express 应用中得到了广泛使用,从提供错误处理静态文件、到压缩 HTTP 响应等等。路由函数可以通过向 HTTP 客户端返回一些响应来结束 HTTP“请求 - 响应”周期,而中间件函数*通常是*对请求或响应执行某些操作,然后调用“栈”里的下一个函数,可能是其他中间件或路由处理器。中间件的调用顺序由应用开发者决定。 > **备注:** 中间件可以执行任何操作,运行任何代码,更改请求和响应对象,也可以**结束“请求 - 响应”周期**。如果它没有结束循环,则必须调用 `next()` 将控制传递给下一个中间件函数(否则请求将成为悬挂请求)。 要使用第三方中间件,首先需要使用 NPM 将其安装到当前应用中。比如,要安装 [morgan](https://www.expressjs.com.cn/resources/middleware/morgan.html) HTTP 请求记录器中间件,可以这样做: ```javascript npm install morgan ``` 然后,你可以对 *Express 应用对象*调用 `use()` 将该中间件添加到栈: ```js const express = require('express'); const logger = require('morgan'); const app = express(); app.use(logger('dev')); ... ``` > **备注:** 中间件和路由函数是按声明顺序调用的。一些中间件的引入顺序很重要(例如,如果会话中间件依赖于 cookie 中间件,则必须先添加 cookie 处理器)。绝大多数情况下要先调用中间件后设置路由,否则路由处理器将无法访问中间件的功能。 中间件函数和路由处理回调之间的**唯一**区别是:中间件函数有第三个参数 `next`,在中间件不会结束请求周期时应调用这个 `next`(它包含中间件函数调用后应调用的**下一个**函数)。 可以使用 `app.use()` 或 `app.add()` 将一个中间件函数添加至处理链中,这取决于中间件是应用于所有响应的,还是应用于特定 HTTP 动词(`GET`,`POST`等)响应的。可以为两种情况指定相同的路由,但在调用 `app.use()` 时路由可以省略。 ```js const express = require("express"); const app = express(); // 示例中间件函数 const a_middleware_function = (req, res, next) => { // ... 进行一些操作 next(); // 调用 next() ,Express 将调用处理链中下一个中间件函数。 }; // 用 use() 为所有的路由和动词添加该函数 app.use(a_middleware_function); // 用 use() 为一个特定的路由添加该函数 app.use("/someroute", a_middleware_function); // 为一个特定的 HTTP 动词和路由添加该函数 app.get("/", a_middleware_function); app.listen(3000); ``` ### 托管静态文件 可以使用 express.static 中间件来托管静态文件,包括图片、CSS 以及 JavaScript 文件(其实 `static()` 是 Express 提供的**原生**中间件函数之一)。例如,可以通过下面一行来托管 'public' 文件夹(应位于 Node 调用的同一级)中的文件: ```js app.use(express.static("public")); //现在 'public' 文件夹下的所有文件均可通过在根 URL 后直接添加文件名来访问了,比如: http://localhost:3000/images/dog.jpg http://localhost:3000/css/style.css http://localhost:3000/js/app.js http://localhost:3000/about.html ``` 还可以为静态 URL 创建一个虚拟的前缀,而不是直接把文件添加到根 URL 里 ```js app.use("/media", express.static("public")); //现在可以通过 '/media' 路径前缀来访问 'public' 文件夹中的文件 http://localhost:3000/media/images/dog.jpg http://localhost:3000/media/video/cat.mp4 http://localhost:3000/media/cry.mp3 ``` 可以通过多次调用 `static()` 来托管多个文件夹。如果一个中间件函数找不到某个文件,将直接传递给下一个中间件(中间件的调用顺序取决于声明顺序)。 ```js app.use(express.static("public")); app.use(express.static("media")); ``` ### 错误处理 用来处理错误的特殊中间件函数有四个参数`(err, req, res, next)`,而不是之前的三个。例如: ```js app.use((err, req, res, next) => { console.error(err.stack); res.status(500).send("出错了!"); }); ``` 错误处理中间件可以任何所需内容,但是必须在所有其他 `app.use()` 和路由调用后才能调用,因此它们是需求处理过程中最后的中间件。 Express 内建了错误处理机制,可以协助处理 app 中没有被处理的错误。默认的错误处理中间件函数在中间件函数栈的末尾。如果一个错误传递给 `next()` 而没有用错误处理器来处理它,内建处理机制将启动,栈跟踪的错误将回写给客户端。 > **备注:** 生产环境中不保留栈跟踪轨迹。可将环境变量 `NODE_ENV` 设置为 `'production'` 来运行所需的生产环境。 > **备注:** HTTP 404 和其他“错误”状态码不作为错误处理。可使用中间件来自行处理这些状态。 ### 文件结构 Express 不对文件结构和组件的选用做任何约定。路由、视图、静态文件,以及其他应用具体逻辑均可按任意文件结构保存在任意数量的文件中。当然可以让整个 Express 应用保存在单一文件中,但是一般情况下,把应用按功能(比如账户管理、博客、论坛)和架构问题域(比如 MVC 架构 中的模型、视图、控制器)进行拆分是有意义的。
顶部
收展
底部
[TOC]
目录
Express/Node 入门
路由
中间件
API接口 express()
API接口 app()
API接口 res()
API接口 Router()
数据库操作 ODM mongoose
相关推荐
Node.js教程:新手入门
Node.js接口
朴灵《深入浅出 Node.js》