在介绍了那么多 Express 核心概念以后,接下来的文章将会把注意力放在如何构建一个真实的应用上。这里咱们先从构建应用 API 接口开始。从某种程度上来讲几乎全部的软件应用其背后都是由一组强大的 API 驱动。html
其实 API 就是一种代码之间交互的一种方式,它既能够是在程序内部也能够是经过网络的跨机器进行。例如,Express 中的 app.use 和 app.get 就属于在内部使用 API 。而经过 HTTP 或者 FTP 等协议发送 JSON、XML 数据的方式则属于后者。对于后一种方式须要注意的是,API 的提供者和使用者必须对数据格式作出约定。在本文示例中,咱们将会讨论如何使用 Express 构建后一类型的 API 接口,同时全部 HTTP 接口返回的数据格式都将使用 JSON。node
另外,本章还会讨论如何设计一个优雅的 API 用于提高使用者的体验和效率,让 API 的含义一目了然而不用去阅读又臭又长的说明文档。就像“好代码”与“坏代码”同样,API 是否优雅其实更多的取决于实际情形。盲目遵循 API 设计的最佳实践有时会显得很迂腐,由于它有可能与使用者的指望不一致。web
接下来的内容包括:数据库
首先,咱们须要明确该示例的功能以及 API 的使用方式,后面再写代码。express
假设,如今程序须要在接受到 America/Los_Angeles 或 Europe/London 等表明时区的字符串后,返回该时区的当前时间信息(例如:2015-04-07T20:09:58-07:00 )。该返回信息与现实中易懂的时间格式是不同的,由于它是为计算机设计的。json
经过相似下面格式的 URL 的 HTTP 请求来调用应用 API:api
/timezone?tz=America+Los_Angeles复制代码
而服务端 API 返回的 JSON 的数据格式,以下:浏览器
{
"time": "2015-06-09T16:20:00+01:00",
"zone": "America/Los_Angeles"
}复制代码
只要能调用 API 并对 JSON 数据进行解析,你就能够在任意平台构建任意应用程序。以下图,你能够经过 AJAX 请求该 API 实现一个展现时区信息的单页应用。bash
你也能够利用该接口实现下图所示的移动应用。服务器
你甚至能够利用该 API 实现下图同样的终端命令行工具:在终端中打印服务端 API 接口返回的数据。
像前一章的天气应用同样,咱们能够利用这些 API 返回的冰冷数据构建更具表达力的 UI 。
了解 API 概念以后,下面咱们就动手实现一个 Express 驱动的 API 服务。实现的原理很是简单:经过中间件和内置函数解析网络请求并将 JSON 数据和 HTTP 状态码封装到响应对象并返回给客户端。
从技术角度上说,API 服务除了使用 JSON 格式外,你还能够是使用 XML 或者纯文本。可是 Express 和 JavaScript 对 JSON 的支持是最好的,同时它也是当前最流行的格式,因此后面会一直使用 JSON 做为默认数据格式。
下面咱们编写一个为多平台提供随机数生成的服务,该 API 将拥有以下特性:
你可能认为这里彻底可使用纯文原本替换 JSON 格式。可是发送 JSON 数据是开发者的必备技能,并且 JSON 格式极易拓展。
该工程的构建步骤以下:
首先,在新建的 package.json 中,复制下面的内容并按照依赖项:
{
"name": "random-number-api",
"private": true,
"scripts": {
"start": "node app"
},
"dependencies": {
"express": "^5.0.0"
}
}复制代码
接下来,将下面的代码复制到入口文件 app.js 中:
var express = require("express");
var app = express();
app.get("/random/:min/:max", function(req, res) {
var min = parseInt(req.params.min);
var max = parseInt(req.params.max);
if (isNaN(min) || isNaN(max)) {
res.status(400);
res.json({ error: "Bad request." });
return;
}
var result = Math.round((Math.random() * (max - min)) + min);
res.json({ result: result });
});
app.listen(3000, function() {
console.log("App started on port 3000");
});复制代码
如今启动应用并访问 http://localhost:3000/random/10/100 的话,你将看到一个附带 10 ~ 100 范围内随机数的 JSON 数据。
接下来,咱们来分析上面的代码。
与以前同样,前两行代码引入了 Express 并建立了一个 Express 应用实例。
而后,咱们建立了一个路由中间件用于处理相似 /random/10/100 这样的 API 请求。固然,这里还存在一些 bug ,例如,没有过滤掉 /random/foo/bar 请求。因此,在调用 API 的时候请确保使用的参数是整型变量。
在而后,咱们使用内置的 parseInt 解析范围参数,而该函数的返回值只多是整形数字或者 NaN。若是传入的参数有一个为 NaN 的话就会给客户端返回一个错误信息。下面这部分代码对于整个程序来讲是很是重要的:
if (isNaN(min) || isNaN(max)) {
res.status(400);
res.json({ error: "Bad request." });
return;
}复制代码
若是上面的参数检查的结果是最少有一个为 NaN ,程序就会进行以下处理:
在代码的最后,咱们会在合法的参数返回内生成随机数并将结果返回给客户端。
虽然示例很简单,可是它已经包含了使用 Express 构建 API 的基本流程:解析请求,设置 HTTP 状态码,返回响应数据。你能够在这个基础之上构建更为复杂优雅的 API 。
CURD 是对程序中 Create、Read、Update、Delete 四种业务动做的一个简称。
大多数的应用都会涉及到 CURD 操做。例如,对于一个图片分享应用来讲,其中涉及图片的全部操做就是典型的 CRUD:
不管是分享照片的社交应用仍是文件存储服务,你生活中的使用的不少服务中都使用了这种模式。不过在开始讨论构建 CRUD 功能的 API 以前,咱们先来看看被称为 HTTP 方法的内容。
HTTP 的规范中是这样定义其方法的:
HTTP 方法明确了对请求 URI 所标识资源进行的操做,并且方法是区分大小写的。
一个更易理解的解释是:客户端在发送 HTTP 请求时须要指定一个 HTTP 方法,而后服务端回依据不一样的 HTTP 方法作出不一样的响应。虽然,可用的 HTTP 方法有不少,可是经常使用的其实并很少。其中在 Web 应用中经常使用是下面 4 个:
虽然 HTTP 还有不少其余的方法,可是它们在现实开发过程当中并不常见。理论上你甚至能够只使用 GET 和 POST 请求完成全部业务,可是这是错误实践毕竟它违反了 HTTP 规范也会给开发者形成困惑。另外,不少浏览器也是根据 HTTP 方法来明确所执行的操做类型。因此,即便并无强制你也应该参照该规范来约束本身的行为。
前面你已经见过 Express 中对部分方法的处理,不过下面的代码将一次涵盖上面全部的四个方法:
var express = express("express");
var app = express();
app.get("/", function(req, res) {
res.send("you just sent a GET request, friend");
});
app.post("/", function(req, res) {
res.send("a POST request? nice");
});
app.put("/", function(req, res) {
res.send("i don't see a lot of PUT requests anymore");
});
app.delete("/", function(req, res) {
res.send("oh my, a DELETE??");
});
app.listen(3000, function() {
console.log("App is listening on port 3000");
});复制代码
将代码复制到入口文件 app.js 中并启动服务,而后你就可使用 cURL 命令测试不一样的 HTTP 方法了。默认状况下 cURL 使用 GET 发送请求,可是你可使用 -X 选项来指定其余的方法。例如,curl -X PUT http://localhost:3000 。
回想如下以前的照片分享应用,下面是其中可能的 CRUD 操做:
不难看出 CRUD 操做与以前四种 HTTP 方法存在对应关系:
所以经过这四个 HTTP 方法咱们能够很好的实现最多见 CRUD 风格的 web 应用程序。
实际上对于更新和建立动做与 HTTP 方法的对应关系,一些人有着本身的见解。它们认为 PUT 更应该对应建立动做而非 POST。另外,新的 PATCH 方法则对应更新操做。虽然本文将会使用上面那种更规范的对应关系,可是你彻底能够按照本身的意愿选择。
为了应对将来可能的 API 更新,对 API 进行版本控制是一件很是高效的方法。例如,前面获取指定时区当前时间的 API 在推出后就被不少的厂商和开发者使用。可是,几年几后因为某些缘由必须对该 API 进行更新而与此同时你又不能影响以前的使用者。此时,咱们就能够经过添加新版原本解决这个问题。其中原有的 API 请求能够经过:
/v1/timezone
而新版本 API 请求则可使用:
/v2/timezone
这样不只在进行 API 更新时防止了代码的破坏性更改。并且接口使用者也有了更灵活的选择,他们能够在必要的时候进行 API 切换。
在 Express 中可使用 Router 中间件来实现 API 版本管理。拷贝下面代码到文件 app1.js 中,并讲其做为第一个版本 API 的实现:
var express = require("express");
var api = express.Router();
api.get("/timezone", function(req, res) {
res.send("Sample response for /timezone");
});
api.get("/all_timezones", function(req, res) {
res.send("Sample response for /all_timezones");
});
module.exports = api;复制代码
请注意,上面的中间件代码在处理的 URL 并无包含 /v1 。下面在入口文件中引入这个 Router 中间件并进行路由映射。
var express = require("express");
var apiVersion1 = require("./api1.js");
var app = express();
app.use("/v1", apiVersion1);
app.listen(3000, function() {
console.log("App started on port 3000");
});复制代码
而后,你将最新版本的 API 实现放在 api2.js 文件中:
var express = require("express");
var api = express.Router();
api.get("/timezone", function(req, res) {
res.send("API 2: super cool new response for /timezone");
});
module.exports = api;复制代码
最后,经过 Router 将这两个版本的 API 同时添加到主入口中:
var express = require("express");
var apiVersion1 = require("./api1.js");
var apiVersion2 = require("./api2.js");
var app = express();
app.use("/v1", apiVersion1);
app.use("/v2", apiVersion2);
app.listen(3000, function() {
console.log("App started on port 3000");
});复制代码
你能够经过浏览器验证这些版本化后的 API 是否正确工做,另外你也可使用 cURL 命令进行测试。
就像前面章节介绍的那样,Router 可让你将不一样的路由存放在不一样文件中进行管理。而版本化 API 就是最典型的应用实例。
每个 HTTP 响应都应该附带一个 HTTP 状态码,其中最有名的就是 404 Not Found 。
虽然 404 是最出名的,可是 200 状态码确是最多见的。与 404 不一样的是,虽然当网页成功加载或 JSON 数据成功返回后都会包含状态码 200,但它并不会被展现出来。
固然,除了 404 和 200 以外,HTTP 中还定义了不少其余的状态码,包括 100、200、300、400 以及 500 系列。须要注意的是并非每一个系列中全部 100 个数字都有明肯定义,例如,100 系列只有 100,101,102 三个有效码,紧跟其后就是 200 。
每一个状态码系列其实都有特定的含义和主题,总结就是:
1xx: 成功接收到请求。
2xx: 成功
3xx: 重定向
4xx: 客户端错误
5xx: 服务端错误
规范中只定义的大约 60 个状态码。你能够在此基础上拓展本身的状态码,可是一般并不会这么作。由于优秀的 API 的首要设计原则就是确保不会对使用者形成任何歧义,因此应该最大程度遵循官方规范的指导。后面咱们会对上面的每一个区间的状态码进行讲解,可是在此以前先来看看如何在 Express 中设置状态码。
少部分应用还在使用 HTTP 1.0 版本的协议,而大部分以及切换到了 1.1 版本。做为下一个版本的 HTTP 2.0 标准如今也逐渐在推广过程当中。幸运的是,2.0 版本的协议大部分更新都在底层因此切换时并不会涉及太大的工做量。另外,2.0 版本还新增了一个 421 的状态码。
默认状况下,HTTP 状态码是 200。若是用户访问的 URL 对应资源不存在的话,Express 会发送 404 错误。若是访问的服务器出现问题的话,Express 就会发送 500 错误。
可是这些都是 Express 的默认行为,某些情形下可能会须要自行设置状态码。为此,Express 的 response 对象提供了一个 status 方法,你须要在调用是传入对应状态码就能完成设置。
// ...
res.status(404);
// ...复制代码
该方法能够进行链式调用,因此你能够紧跟其后使用 json 设置返回的数据。
res.status(404).json({ error: "Resource not found!" });
// 它等价于:
res.status(404);
res.json({ error: "Resource not found!" });复制代码
虽然 Express 对原生 Node 的 response 对象进行了拓展,而且在使用 Express 时也应遵循 Express 风格,可是你依旧可使用原生方法来完成设置。
res.statusCode = 404;复制代码
100 区间的官方状态码只有两个:100(继续) 和 101 (切换协议),并且它们不多会被用到。若是你必须处理的话,能够去官网或者维基上查看。
200 区间状态码表示请求成功。虽然该区间状态码很多,可是经常使用的也就下面 4 个:
一样,在 300 区间,咱们只介绍其中经常使用的三个,而且它们全都涉及重定向。
400 区间的状态码是最多的,而它一般都是表示因为客户端的错误致使请求失败。
至于该区间其余状态码,读者能够去维基上自行查看,这里就不一一介绍了。另外,当你不肯定应该使用哪一种客户端错误状态码时,你能够直接使用 400 。
做为 HTTP 规范里的最后一个区间,500 区间状态码表示的是服务内部出现错误。例如,请求过载或者数据库链接中断。另外,理论上该区间的错误只能有服务内部本身触发。最后,为了防止黑客窥探太多内部信息,你能够对全部的内部错误仅仅返回一个抽象的“内部服务器错误”这样的信息。
本章包含的内容有:
原文地址