🦄 2024 独立开发者训练营,一起创业!查看介绍 / 立即报名(剩余10个优惠名额) →

Node.js 服务端应用开发 #8:服务端与客户端交换数据

客户端与服务端之间一般会用 HTTP 这种应用协议沟通交流。 HTTP 协议提供了一些方法,比如 GET(获取),POST(发布),PUT(覆盖),PATCH(修正) 还有 DELETE(删除)都是 HTTP 里的一些方法。在服务端定义服务接口的时候,可以设置接口支持用哪一种 HTTP 的方法使用它。

客户端与服务端之间最常用的交换数据的方法就是通过请求与响应,客户端向服务端发出请求,服务端可以做出响应。客户端可以向服务端请求它需要的数据,或者把客户端上的数据发给服务端。服务端根据请求的地址可以决定要去做什么事情,比如它可以给客户端准备一些数据,然后发给客户端。

请求与响应主要由两部分组成,头部(Header)与主体(Body)。请求可以把要给服务端的数据放到请求的主体里面,请求的头部里面会包含请求相关的一些信息,比如服务端给用户签发的令牌就可以放在请求的头部里面。服务端给客户端做出的响应里面,可以把客户端需要的数据放在响应的主体里面,然后可以通过响应的头部信息告诉客户端一些额外的事情,比如响应的数据是什么格式的。

服务端给客户端做出响应的时候,可以设置一个响应的状态码(Status Code),HTTP 协议规定了一些状态码,分别表示不同的意义,比如 200 表示请求成功被处理了,404 表示没找到资源,401 表示未授权等等。客户端收到响应之后,可以根据响应的状态码再决定要做什么事情。

准备

任务:准备项目<http>

在项目里创建一个 http 分支,并把当前分支切换到这个新创建的分支上,下面每完成一个任务以后,就在这个分支上做一次提交。在终端,项目所在目录的下面,执行:

git checkout -b http

HTTP 客户端

我们要开发的服务端应用需要创建一些应用接口(API),应用的客户端可以使用这些接口提供的功能。在开发这些应用接口的时候,需要做一些测试,如果我们还没有做好应用的客户端,在测试应用接口的时候可以借助一些 HTTP 客户端软件或者叫接口测试客户端软件。这样即使我们没有应用的客户端,也可以测试使用服务端应用的接口。

之前我们创建过几个应用接口,测试它们的时候用的是浏览器,不过浏览器并不是最好的选择。因为我们的接口可能要使用不同的 HTTP 方法发出请求,请求的时候可能需要在客户端这里提供一些数据,所以我们需要一款专业的接口测试客户端软件。现在常用的有两个选择,Postman 还有 Insomnia。

任务:准备一个 HTTP 客户端软件<Insomnia>

在后面我们会选择使用 Insomnia 这个 HTTP 客户端软件来测试我们开发的应用接口,所以你现在可以去到它们官方网站下载一个,把它安装在自己的电脑上。

官方网站https://insomnia.rest/

Insomnia 的基本用法

打开 Insomnia 之后,可以新建一个 Request(请求),随便给这个请求起个名字,然后配置一下请求用的 HTTP 的方法,还有请求的接口地址。如果接口需要在客户端提供一些数据,我们可以在请求里设置它的 Body(主体),如果接口需要验证用户的身份,可以配置请求的 Auth(身份验证),我们还可以在请求里添加一些头部,最后发送配置好的请求,可以观察服务接口发回来的响应。

工作区:Workspace

在 Insomnia 里面,我们可以根据开发的项目创建不同的 Workspace(工作区),在工作区里可以添加一些请求,用这些请求测试我们开发的服务端应用的不同的接口。工作区不是必须的,我们可以直接在默认的工作区里创建请求来测试接口,不过可能会有些乱套,所以我们可以创建不同的工作区,然后分别在这些工作区里创建各自的请求。

比如我们可以在 Insomnia 里面,创建一个叫 XB2 的工作区,在后面我们会在这个工作区里添加一些请求来测试使用我们开发的服务端应用接口。默认的工作区叫 Insomnia,点击窗口左上角的 Insomnia 会弹出一个菜单,选择 Create Workspace,创建一个名字叫 XB2 的工作区。

环境:Environment

在 Insomnia 这个 HTTP 客户端软件里面,可以创建一些不同的环境,比如开发环境,生产环境。在这些环境里可以定义一些值,这些值可以用在创建的请求里面。我们可以切换使用不同的环境。

在请求里可能会重复用到一些东西,比如请求的接口地址里的基础部分都是一样的,我们在本地开发的这个应用,基本的地址就是 http://localhost:3000。在不同的请求里都会用到这个地址,而且这个地址在不同的环境里可能会不一样,比如把应用部署到真实的服务器上,这个地址可能就变成别的了,比如 https://xb2-node-api.ninghao.net

这种情况我们就可以在工作区里创建两个环境,然后在这两个环境里分别定义一个表示地址基本部分的值,这个值可以用在不同的请求里面。在工作区里切换使用不同的环境,这个值就会是当前环境下的值。

点击左边栏上的 No Environment,在弹出的菜单里,选择 Manage Environments(管理环境),然后 Sub Environments 的下面创建两个新的环境,一个是 开发环境,一个是 生产环境,再分别配置一下这两个环境下的值。

开发环境

{
  "xb2_api": "http://localhost:3000"
}

生产环境

{
  "xb2_api": "https://xb2-node-api.ninghao.net"
}

上面我们在两个不同的环境下面,同时都定义了 xb2_api,它的值在不同环境下是不一样的,在后面创建的请求地址里我们可以使用这个 xb2_api,这样切换不同的环境,请求的地址就会是对应的在环境里定义的地址了。

创建了环境之后就可以切换使用不同的环境了,先选择 开发环境。

请求:Request

在工作区里可以创建一些 Request(请求)来测试使用我们开发的不同的应用。请求可以使用 Folder(目录 / 文件夹)来整理,比如我们先创建一个 Folder 叫内容,然后在这个目录里创建一个请求,请求的名字叫内容列表,请求用的 HTTP 方法设置成 GET,请求的地址设置成 xb2_api/posts,注意这里用了我们在环境里定义的一个值,就是这个 xb2_api,在开发环境里,它的值是 http://localhost:3000 。

HTTP 方法

客户端与服务端之间一般会用 HTTP 这种应用协议沟通交流。 HTTP 协议里有一些方法,比如 GET(获取),POST(发布),PUT(覆盖),PATCH(修正) 还有 DELETE(删除)都是 HTTP 里的一些方法。在服务端定义服务接口的时候,可以设置接口支持用哪一种 HTTP 的方法使用它。

一般如果接口的功能是给客户端提供数据,可以选择使用 GET 方法。如果接口是要处理客户端发布的数据,可以选择使用 POST 方法定义这个接口,比如创建新内容用的接口,一般都会选择用 POST 这种方法。更新内容用的接口可以选择 PUT 或者 PATCH 这两个方法。接口如果是删除内容用的,可以选择使用 DELETE 这种 HTTP 方法。

接口要选择使用哪一种 HTTP 方法并不是强制的,也就是一个更新内容用的接口,你可以选择 PUT 或者 PATCH,也可以选择 POST,这个完全取决于我们自己对服务接口的设计。

之前我们已经用 Express 应用框架定义了几个支持用 HTTP 的 GET 方法使用的接口,用的就是 app.get() 这个方法。要定义支持其它的 HTTP 方法使用的接口,可以使用 Express 提供的另外几个方法。比如 app.post() 可以定义支持用 POST 方法使用的接口,app.patch() 可以定义支持用 HTTP 的 PATCH 方法使用的接口,用 app.delete() 可以定义支持用 DELETE 这种方法使用的接口。

在应用里面我们可以根据应用里的内容资源,分别定义一下跟这个资源相关的一些接口。比如我们要做的是一个照片分享应用,用户发的贴子就是应用里的一种内容资源,可以用 posts 表示这种资源。针对这种资源可以定义下面这些内容接口,注意接口的地址都是一样的,也就是资源名字的复数形式,但是接口支持不同的 HTTP 方法。

发布资源

app.post('/posts', (request, response) => {});

接口支持使用 HTTP 的 POST 方法使用,接口地址是 /posts 。客户端在使用这个接口发布新的内容资源的时候,可以把要发布的内容放在请求的主体里面,在接口里面可以获取到主体数据。

更新资源

app.patch('/posts/:postId', (request, response) => {});

接口支持使用 HTTP 的 PATCH 方法使用,接口地址是 /posts/:postId 。客户端在使用这个更新资源的接口时候,可以把更新的内容放在请求的主体里面,这样在接口里面可以获取到主体数据。

删除资源

app.delete('/posts/:postId', (request, response) => {});

接口支持使用 HTTP 的 DELETE 方法使用,接口地址是 /posts/:postId。地址里的 :postId 是它的一个参数,使用这个接口的时候要提供这个地址参数的值,这个值应该就是要删除掉的内容的 ID 号。

资源列表

app.get('/posts', (request, response) => {});

接口支持使用 HTTP 的 GET 方法使用,接口地址是 /posts 。

单个资源

app.get('/posts/:postId', (request, response) => {});

接口支持使用 HTTP 的 GET 方法使用,接口地址是 /posts/:postId。地址里的 :postId 是它的一个参数,使用这个接口的时候要提供这个地址参数的值,这个值就是要获取到的内容数据的 ID 号。

创建内容用的应用接口

在设计创建内容用的接口的时候,可以选择使用 HTTP 的 POST 方法。客户端在使用这种应用接口的时候,要在请求的主体里提供要发布的具体的内容,这些内容就是用户通过用户界面提供的。提供的数据的格式可以选择 JSON,接口可以处理这种格式的数据。

服务端一般会把收到的数据放到自己的数据仓库里,然后给客户端给出一个响应,响应的数据可以是新创建的内容,也可以只是通知一下客户端处理的状态,服务端正常的创建了内容以后,做出的响应的状态码可以是 201,表示成功的创建了内容。

暂时我们先把所有的代码全部放在 src/main.js 这个文件里,后面我们会重构应用。

任务:在应用全局使用 JSON 中间件

在用了 Express 框架的应用里,想让应用接口可以处理 JSON 格式的数据,可以在应用的全局范围使用一个中间件。这个中间件会把客户端那里发过来的 JSON 格式的处理好,这样接口就可以直接使用这些数据了。

src/main.js<添加>

/**
 * 使用 JSON 中间件
 */
app.use(express.json());

你上面这几行代码放在 app.listen() 的上面。这里用了一下 app.use() ,它可以在指定的路径上安排使用一些中间件,这里我们并没有设置具体的路径,所以给它安排的中间件会用在所有请求的上面。这里用的中间件是 express.json(),它可以处理从客户端那里发送过来的 JSON 格式的数据,处理之后,接口的处理器就直接能用请求里的数据了。

任务:定义创建内容资源用的接口<POST>

在定义创建数据用的接口的时候,一般我们都会选择让它支持用 HTTP 的 POST 方法来使用,在 Express 框架里,定义这种接口可以使用 app.post() 方法。

1:定义创建内容接口

src/main.js<添加>

/**
 * 创建内容
 */
app.post('/posts', (request, response) => {});

把上面代码放在 src/main.js 这个文件的底部就可以了。上面用了 app.post() 方法定义了一个支持用 HTTP 的 POST 方法使用的接口,接口地址设置成了 /posts

2:提取请求主体数据

客户端在使用这个接口的时候需要提供用户要发布的内容数据,客户端可以选择使用 JSON 格式的数据,在我们的应用里已经在所有接口上安排使用了可以处理 JSON 格式数据的中间件,所以在接口的处理器里面,我们可以直接使用这些数据,它们会在 request.body 这个属性里面。继续在定义的接口的处理器里面,添加:

// 获取请求里的数据
const { content } = request.body;

上面用了一个解构的写法,把请求主体里的 content 这个属性从请求的主体里拿出来。这里我们假设要创建的内容只需要一个 content 属性,在真实的应用里要创建的内容可能需要很多属性,你可以使用同样的方法,把它们从请求的主体里挑出来用。

3:做出响应

在真实的应用里我们可以把客户端发过来的数据存储在数据仓库里,这里我们只是为了演示客户端与服务端交换数据的方法,所以暂时可以直接响应一个简单的数据,里面用一下客户端发送过来的数据里的 content 这个属性的值。

// 作出响应
response.send({
  message: `成功创建了内容:${content}`,
});

上面用了一下 response.send() ,给客户端发过去一个数据,这个数据里面有个 message 属性,它的值是我们用字符模板制造出来的一行文字,文字里用了一下客户端发过来的 content 属性的值。这个数据会被框架自动转换成 JSON 格式的,然后再把它发给客户端。

任务:测试创建内容资源用的接口

1:添加创建内容请求

在 HTTP 客户端< Insomnia>可以测试一下刚才定义的创建内容用的演示接口。在 内容 目录的下面,新建一个请求,名字是 创建内容,请求用的 HTTP 方法设置成 POST,请求的地址可以设置成 xb2_api/posts ,然后要选择一个 Body(主体),主体数据的类型选择 JSON ,然后输入下面这个主体内容:

{
  "content": "日出江花红胜火,春来江水绿如蓝"
}

2:观察请求里带的头部数据

打开请求的 Header,观察一下。你会发现客户端在请求里添加了一条头部数据,名字是 Content-Type,对应的值是 application/json。服务端应用接口会读取这条头部数据,这样它就知道客户端发送过来的数据是 JSON 格式的了。

3:发送请求,观察响应

点击 Send,发送一下这个 创建内容 请求,客户端这里得到的响应的数据应该是:

{
  "message": "成功创建了内容:日出江花红胜火,春来江水绿如蓝"
}

在客户端请求 创建内容 这个接口,服务端会会组织好一个数据,再把它发送客户端。客户端得到的响应也是一个 JSON 格式的数据,数据里有个 message 属性。

状态码:Status Code

状态码也是服务端与客户端交流的方法,服务端作出的响应里面都会带着一个响应的状态,这个状态是用三位数字表示的。比如在响应里如果状态码是 200,就表示请求成功被服务端处理了,客户端收到响应,可以检查里面的状态码,这样它就可以大概判断出发生了什么。

200 表示成功处理了请求,201 表示成功创建了内容,401 说明需要先验证身份才能处理请求,500 表示服务端那里出了点问题。这些状态码就是服务端告诉给客户端的某种代号,表示的是服务端处理请求的状态。HTTP 规定了一些可以使用的状态码,还有它们表示的意思。我们在开发服务端应用的时候,可以根据需求,使用合适的状态码。

HTTP 状态码参考:httpstatuses.com

任务:设置服务端响应的状态码

在 HTTP 客户端,发送之前我们创建的 创建内容 这个请求,然后观察一下得到的响应里的状态码,你会发现是数字 200 ,这是响应默认使用的一个状态码,表示请求成功被处理了。在服务端如果成功创建了客户端要求的内容,作出的响应的状态码可以设置成 201,表示成功创建了内容。

1:设置响应状态码

在 Express 应用框架里,可以使用 response.status() 设置一下响应的状态码。

src/main.js<修改>

修改一下之前我们定义的 创建内容 接口的处理器,在处理器函数里添加下面这行代码,把它放在 response.send() 的上面,也就是在做出响应之前,可以先设置一下响应里的状态码。

// 设置响应状态码
response.status(201);

上面用了一下 response 上的 status() 方法,它可以设置响应的状态码,这里设置成了 201 ,这个状态码的意思是成功创建了内容。

2:发送请求,观察响应里的状态码

在 HTTP 客户端,重新发送一下 创建内容 这个请求,这次你会发现在得到的响应里面,状态码会是 201 。以后在设计客户端应用的时候,可以让它检查从服务端那里得到的响应的状态码,然后再决定去做不同的事情。

头部数据:Headers

在客户端发出的请求还有服务端做出的响应里面除了主体数据以外,还会带着头部数据,客户端与服务端之间可以用这种方式交换数据。比如服务端应用接口在处理客户端请求的时候,需要验证用户的身份。这样在客户端那里,就可以把这个身份验证的信息放在请求的头部。服务端可以根据请求里的头部数据来判断用户的身份。

任务:在客户端的请求里设置头部数据

在设计服务端应用接口的时候,如果需要,可以要求客户端在请求里提供指定的头部数据。

1:在请求中设置头部数据

在 HTTP 客户端,配置一下 创建内容 这个请求,在 Header 里面添加一条头部数据,名字是 Sing-along,对应的值是 twinkle twinkle little star 。这样在客户端发送这个请求,服务器对应的接口处理器里就可以从请求的头部里得到 Sing-along 这个头部数据的值。

2:在服务端测试获得请求中的头部数据

在服务端应用接口的处理器里面,可以使用通过 request.headers 这个属性获取到客户端请求里的头部数据,或者也可以使用 request.header() 方法获取到指定的头部数据。

src/main.js<修改>

// 输出请求头部数据
console.log(request.headers['sing-along']);

把上面这行代码放在 创建内容 接口的处理器里面,放在作出响应的代码的上面。这行代码做的事情就是在控制台上输出了请求里的一个叫 sing-along 的头部数据,因为这个属性的名字里带小横线,所以访问这个属性的时候我们用了方括号的形式,不然可以直接用点的时候访问对象里的数据。

3:观察在服务端输出的头部数据

在终端,重新启动一下应用,然后再到 HTTP 客户端发送一下之前配置好的 创建内容 这个请求。再回到终端观察一下,你会发现输出了 twinkle twinkle little star 这行文字,这就是在客户端发送请求的时候,在请求里包含的 Sing-along 这个头部数据的值。

任务:在服务端设置响应的头部数据

服务端接口对客户端的请求作出响应的时候,可以在响应的头部里面添加点数据,比如最常见的就是在头部数据里告诉客户端响应的数据的类型是什么,这样客户端收到响应之后可以根据头部数据里的内容类型来判断如何处理这个响应里的数据。

1:设置响应里的头部数据

在 Express 框架里,在响应里设置头部数据可以使用 response.set() 这个方法。

src/main.js<添加>

// 设置响应头部数据
response.set('Sing-Along', 'How I wonder what you are!');

把上面这行代码放在 创建内容 接口的处理器里面,放在作出响应的代码的上面。这里用的是 response.set() 方法,第一个参数是要在响应里添加的头部数据的名字,第二个参数就是给这个头部数据的具体的值。

2:在客户端观察响应里的头部数据

在 HTTP 客户端,发送 创建内容 这个请求,然后观察得到的响应里的 Header,在里面你可以找到一个名字是 Sign-Along 的头部数据,它的值是 How I wonder what you are! 这是我们在应用接口的处理器里用 response.set() 添加到响应里的头部数据。客户端可以读取响应里的头部数据,再去做它想做的事情。

总结

客户端与服务端之间可以使用请求与响应的方式来交换数据,客户端可以请求让服务端去做一些事情,可以管服务端要一些数据,也可以让服务端把客户端上的数据存储起来。服务端在定义自己的应用接口的时候,可以选择使用一种特定的 HTTP 方法,这个完全取决于接口的设计。可以使用的方法有 GET,POST,PUT,PATCH 还有 DELETE。

客户端与服务端交换的数据一般可以选择使用 JSON 格式,但这不是必须的,还有很多其它类型的数据格式,使用什么类型的数据格式取决于服务端应用接口的设计。不管用什么,客户端都要按照服务端接口的设计使用这些接口,比如使用哪一种 HTTP 方法请求使用接口,请求主体里的数据的格式,请求里要包含哪些头部数据,请求的地址,请求得到的响应可能会是什么样的。

客户端与服务端可以通过头部来交换一些数据,在客户端的请求还有服务端的响应里都可以包含一些头部数据,对方可以利用这些头部数据。比如客户端可以把用户身份信息放在请求的头部里,服务端可以把响应的数据类型放在响应的头部里。

服务端给客户端做出的响应里面会包含一个状态码,这个状态码是用三位数字表示的,不同的状态码都有各自的意思,比如 200 表示请求成功被处理了,201 表示成功创建了内容,404 表示请求的资源不存在,500 表示服务端挂了。

下一站,我们要改进一下应用的开发工作流程,比如在项目里使用 TypeScript,自动重启服务,自动格式化代码等等。

任务:整理项目

确定之前对项目做的修改全部做了提交,并且项目当前没有任何修改。然后把当前分支切换到 develop,执行:

git checkout develop

在 develop 分支上做一次合并:

git merge http

如果你为项目添加了远程,可以把 http 分支推送到远程,执行:

git push origin http

参考课程

https://ninghao.net/course/8214

微信好友

用微信扫描二维码,
加我好友。

微信公众号

用微信扫描二维码,
订阅宁皓网公众号。

240746680

用 QQ 扫描二维码,
加入宁皓网 QQ 群。

统计

15260
分钟
0
你学会了
0%
完成

社会化网络

关于

微信订阅号

扫描微信二维码关注宁皓网,每天进步一点