切换视频源:

在上一章中,我们演示了如何通过公开的 API 获得 JSON 数据,比如访问以下的URL就能获得一些和 post 相关的 JSON 数据:

https://jsonplaceholder.typicode.com/posts/

在这一章中,我们就来搭建后端服务器,并创建自己的 API,学完这章节的内容,你就能完全理解全栈开发啦!

REST API

什么是 REST API 呢?最简单的说法就是通过网络的 HTTP 协议进行客户端和服务器之间的数据交换。假设一个电子图书馆网站前后端的交互是通过 REST 实现的,那么从前端获取一本书籍的信息,只需要先后端的服务器发送一个 HTTP 的 GET 请求给地址 /book/234,就会得到书籍id为 234 的具体书籍信息:

{
    "title": "Artificial Intelligence",
    "book_id": "234",
    "author": "Enoch",
    "published_year": 1995,
    ...
}

拿到数据后,前端就能使用新数据改变前端的展示了,如果这个软件有多个客户端(游览器版,桌面版,手机版),那么当各种前端需要此类数据的时候,只需要使用相同的 API 就可以了。

如果不使用 REST 的形式开发,传统Web开发模式中,如果前端需要电子书的信息,则需要向后端发出请求,然后后端重新生成一个HTML网页返回给前端,速度就会慢很多。而采用 REST 形式开发软件主要有以下两点好处:

  • API 的复用率更高:任何形式的软件(游览器,手机,桌面)都可以和相同的API交互。
  • 总是可用:只要有网络,数据的交流就不会断。

什么是 REST 构架?

那 REST 具体的含义是什么呢?首先要明白的是,REST 是一种软件构架模式,这种方法定义了开发软件的方式。要理解 REST 构架,最好的方式就是理解其全称 Representational State Transfer 词组的意思,只要搞懂每个词的意义,就能了解其设计原理了。

资源(Resources)

REST的全名是“表现层状态转化”,其中省略了主语,“表现层”其实指的就是“资源”的“表现层”。

所谓“资源”,指的是网络上的一个实体,或者说是某个具体信息。实体可以是一段文本、一张图片、一首歌曲、一种服务,总之就是以某种形式存在的具体信息。你可以用特定的 URL 指向每种资源,若要获取某个资源,访问指向它的 URL 就可以了。这样,前端和后端之间的信息“互动”,只需要通过调用不同 URL 即可。

表现层(Representation)

“资源”是一种信息实体,它可以有多种表现形式,比如 text,、HTML、XML、JSON等等。

URL 只代表资源的实体,不代表其形式。严格来说,URL 后面的”.html”后缀名是不必要的,因为后缀名表现格式,属于“表现层”范畴,而 URL 只应该代表“资源”的位置。而它的具体表现形式,应该在 HTTP 请求的头信息中用 Accept 和 Content-Type 字段指定,这两个字段才是对“表现层”的描述。

状态转化 (State Transfer)

访问一个网站,就代表了客户端和服务器间的互动过程。在这个过程中,势必涉及到数据和状态的变化。

互联网通信协议 HTTP 协议,是一个无状态协议。这意味着,所有的状态都保存在服务器端。因此,如果客户端想要操作服务器,必须通过某种手段,让服务器端发生“状态转化”(State Transfer)。而这种转化是建立在表现层上的,所以就是“表现层状态转化”。

客户端用到的手段,只能是 HTTP 协议。具体来说,就是 HTTP 协议里面,四个表示操作方式的动词:GET、PUT、POST、DELETE。它的分别对应四种基本操作:GET用来获取资源,PUT用来新建资源(也可以用来更新资源),POST用来更新资源,DELETE用来删除资源。

综合上面的解释,我们总结一下什么是 REST 架构:

  • 每一个 URL 代表一种资源
  • 客户端和服务端之间,传递这种资源的某种表现层
  • 客户端通过四个 HTTP 动词,对服务器端资源进行操作,实现“表现层状态转化”。

REST API 例子

明白了REST的基本概念,我们来解析一下之前调用的这个 API:

https://jsonplaceholder.typicode.com/posts/

其实这个 API 指定的实体是一些发布的POST内容,当我们在游览器的地址栏输入这个 URL 后,游览器会自动发出 HTTP GET Reqeust,所以结果是获取信息。

如果想要测试更多和 API 相关的功能,可以登入这个网站下载工具 Postman,实现各种不同的 HTTP 请求(GET、PUT、POST、DELETE 等等)。

那么接下来,我们就来学习如何使用 NodeJS 进行 REST API 的创建。

NodeJS

NodeJS 简介

根据我们目前的学习,我们了解了JavaScript是门前端动态语言,接下来要学习的NodeJS,有了它,我们就可以直接在后端使用JavaScript了。

简单来说,Node是JavaScript语言的服务器运行环境。没有Node,JavaScript只能作为游览器上的前端语言,有了Node,JavaScript也可以用于服务器端的开发。

所谓”运行环境“有两层意思:首先,JavaScript语言可以通过Node在服务器运行,在这个意义上,Node有点像JavaScript虚拟机;其次,Node提供了大量工具库,使得JavaScript语言能与操作系统互动(比如读写文件、创建进程),在这个意义上,Node又是JavaScript的工具库。

Node内部采用Google公司的V8引擎,作为JavaScript语言解释器;通过自行开发的libuv库,调用操作系统资源。

Node和NodeJS是一样东西,大家不要纠结其名字。

安装 Node 和 Npm

以下是 NodeJS 的安装步骤:

  1. 登入NodeJS官方下载地址
  2. 下载任意适合所属操作系统的版本(左边是稳定版)
  3. 然后打开下载后的安装包,按照默认提示安装

安装好之后,Mac 用户可以打开 Terminal,输入 node -v 命令,如果有版本号出现,就代表安装成功了:

$ node -v 
v12.18.3

大家可以尝试使用以下的命令,执行第一段非常简单的 Node 代码:

$ node -e 'console.log("Hello World!")'
Hello World!

安装完Node之后,我们还需要下载 NodeJS 的包管理器 NPM,用来直接下载别人写好的代码和库,有了 NPM,我们就能直接调用别人的库,使用现成的轮子。其实 npm 在安装 NodeJS 的时候就顺带装好了,只要我们在终端中输入 npm -v,就会输出npm的版本号:

$ npm -v
6.14.6

第一个 Node 程序

既然 Node 只是 JavaScript 的运行环境,所以我们写的后端程序其实就是 JavaScript 程序,语法是一模一样的,文件名也是以 .js 结尾。

在 NodeJS 中,模块的概念非常重要,指的是别人写好的库(代码),以下这段代码使用 require 函数调用了 http 模块,然后使用其模块创建本地服务器:

var http = require('http');

//create a server object:
http.createServer(function (req, res) {
  res.writeHead(200, {'Content-Type': 'text/html'});
  res.write('Hello World!'); //write a response to the client
  res.end(); //end the response
}).listen(8080); //the server object listens on port 8080

console.log('The server is running...')

大家可以将其代码放入 hellp.js 文件中,然后在 terminal 中输入:

$ node hello.js
The server is running...

这就代表终端提示服务器开启了,如果在游览器的地址栏输入 localhost:8080/,就会弹出 Hello World! 字符串,那么恭喜你完成了服务器的创建!

用 ExpressJS 实现 REST API

除了用自带的 http 模块来处理 http 请求,我们也可以使用现成的包处理客户端发来的网络请求。现在最流行的包就是 express,大家可以在terminal中使用 npm 安装这个包:

npm install express

假设我们想要从后端传一个 JSON 文件给前端,我们先在本地创建好要被传输的 JSON 文件:

[{
    "name": "Il Brigante",
    "rating": "5.0",
    "match": "87",
    "cuisine": "Italian",
    "imageUrl": "/image-0.png"
}, {
    "name": "Giardino Doro Ristorante",
    "rating": "5.0",
    "match": "87",
    "cuisine": "Italian",
    "imageUrl": "/image-1.png"
}]

然后编辑以下代码,实现用于获得此文件的REST API:

var express = require('express');
var app = express();
const port = 8080

app.get('/json_data', function(req, res) {
    const data = require('./data.json'); 
    res.json(data);
});

app.listen(port, () => {
    console.log(`Example app listening at http://localhost:${port}`)
})

运行此文件后,如果大家使用游览器打开 localhost:8080/json_data,就会看到 JSON 文件中的信息。这就实现了我们的第一个 REST API(通过Get获取实体的信息)!

为了传递给后端更多的信息,我们可以在网址中放入额外的参数。若要添加参数,需要在网址后添加一个问号 (?),参数的内容跟在问号之后,每个参数的格式是para_name=para_value,para_name代表参数名,para_value代表参数的值。不同参数之间需要以 & 符号分隔开:

https://jsonplaceholder.typicode.com/posts?userId=1&id=2

以上这个api中有两个参数,一个是userId,值为1,另一个是id,值为2。这个API表明我们只需要那些userId为1,postId为2的post。使用Express可以很方便地获取网址中的参数:

app.get('/parameters', function(req, res) {
    const head_info = req.query.head;
    const para_info = req.query.para;
    head_html = '<h1>' + head_info + '</h1>' 
    paragraph_html = '<p>' + para_info + '</p>' 
    res.send(head_html + paragraph_html)
})

这段代码的作用就是提取parameters之后的两个参数head和para,并把head的值放入h1标签中,把para的值放入p标签中,最后返回合并后的内容。运行文件后,大家可以尝试这个api:http://localhost:8080/parameters?head=Head&para=para ,输入游览器后,会看到Head是标题,para出现在段落中。

express也能很方便地用来处理post request,在 POST 请求中是可以含有 body 的,为了处理 JSON 形式的 body,我们要使用 express.json() :

app.use(express.json()); // json parser for post request

app.post('/handle', function(req, res) {
    console.log(req.body);
    res.json(req.body);
})

大家可以下载 postman 软件向 localhost:8080/handle 发送 post 请求,然后在 body 中加入任何 JSON 格式的内容。发送后服务器会输出收到的 body 内容,并将其内容返回。

可见使用 express 创建 REST API 非常方便,下一章我们就将前后端所有的知识点整合起来,创建一个全栈爬虫程序吧!

实践练习

请使用 NodeJS 创建以下三个API:

  • GET:/json_file?name=xxx 获取特定命名 JSON 文件的内容
  • POST: /json_file?name=xxx 通过POST请求在 body 中传入 JSON 数据,更新特定文件,如文件不存在,则新建文件
  • DELETE:/json_file?name=xxxx 删除特定命名的 JSON 文件

答案请参看:JavaScript实践练习答案