nodejs_restful_api_tutorial

Node.js RESTful API 实例教学: 简单 To Do List

REST 全称是 Representational State Transfer,中文翻译为具象状态传输,是一种 Web 应用程式架构风格,由于它的简洁性,近年越来越多人采用。它的重点是,资料由 URI 指定,并通过 HTTP 提供的 POST , GET 等方法进行操作。符合REST设计风格的 Web API 称为 RESTful API。Node.js 对 REST 是非常有善,也就是说使用 Node.js 来建立 RESTful API 是很容易的事情。

REST是设计风格而不是标准。REST通常基于使用HTTP,URI,和XML以及HTML这些现有的广泛流行的协议和标准。

  • 资源是由URI来指定。
  • 对资源的操作包括获取、创建、修改和删除资源,这些操作正好对应HTTP协议提供的GET、POST、PUT和DELETE方法。
  • 通过操作资源的表现形式来操作资源。
  • 资源的表现形式则是XML或者HTML,取决于读者是机器还是人,是消费web服务的客户软件还是web浏览器。当然也可以是任何其他的格式。

维基百科

在这个练习中,我们要实现一个简单的 RESTful API 服务器,提供待办事项列表(To do List)的各种操作,包括:

  1. 建立一个待办事项
    操作方式:POST;
    Endpoint:/todo;
  2. 读取一个待办事项
    操作方式:GET
    Endpoint:/todo/:id;
  3. 删除一个待办事项
    操作方式:DELETE
    Endpoint:/todo/:id;
  4. 读取整个待办事项列表
    操作方式:GET
    Endpoint:/todo,。
  5. 更新一个待办事项
    操作方式:PUT,
    Endpoint:/todo/:id;

Endpoint 是指网址最后的部分,例如,POST的网址会是:localhost:3000/todo 。

目录

  1. npm init:建立新专案
  2. 建立服务器
  3. RESTful POST 新增一个待办事项
  4. RESTful GET 读取一个待办事项
  5. RESTful DELETE 删除一个待办事项
  6. RESTful GET 读取整个待办事项列表
  7. RESTful PUT 更新一个待办事项
  8. 总结
  9. 下载程式码
  10. Change Log

1. npm init:建立新专案

首先,用 npm init  一个新的 project 。这个指令可以协助你建立一个 package.json 档案,里面包含这个 project 的配置资讯,之后要修改配置可以直接改这个档案。作为练习,我们可以一直按 Enter,使用默认的配置。

npm_init_nodejs_restful

2. 建立服务器

在这个练习中,我们会使用到 http 跟 router 两个 modules,分别用来建立服务器与处理路由。http 是 Node.js 内置的,但 router 则需要透过 npm 安装,在 project 的目录下跑一次以下指令:

等 router 安装完成后,新增一个 server.js 档案,加入以下程式。这段程式的功能是建立一个 server,监听 port 3000,并用 router 对错误进行简单的处理。

然后,回到 Command Line,执行:

会看到 server 成功运行。用浏览器打开:localhost:3000,会看到空白页面,Command Line 没有特别讯息,代表一切正常。 No news is good news。

3. RESTful POST 新增一个待办事项

正常来讲,待办事项应该储存在数据库里,但这里我们只用一个 Array 在存放。以下程式都放在 server.js 里面:

接着,我们要处理第一个路由: /todo,新增一个待办事项:

首先,我们建立一个函式来处理 /todo 路由,这个函式就是这个路由的 handler。在函式里,我们把计数器加 1 ,以追踪待办事项总数。接着,把项目 id 回应出去。最后一行,可以看到,我们要处理的是 POST 的请求(router.post),要针对的Endpoint 是 /todo ,使用的 handler 是刚刚建立的 createItem 函式。

由于这里处理的是 POST 的请求,我们无法直接用浏览器查看效果,而要使 curl 或者一些浏览器的 REST 工具,像是:Advanced REST Client 等,这里我们用 curl 。打开一个新的 Command Line (原本 Server 所在的 Command Line 不能关掉),输入以下指令:

注意, X 是大写,指令很简单,就是向后面的网址执行 POST 请求。马上你就会得到 Item 1 这个讯息作为回应,同时,server 所在的 Command Line 会得到讯息:Create Item 1。把这个指令多执行几次,会看到 Item 数的累加。

3.1 Body Parser

刚刚新增的项目只有编号,并没有俱体内容。我们希望能够通过 RESTful API 把项目内容也传过去,这意味着,服务器(server.js)那边要能够解释传进来的资料。要完成这个任务,可以借助 body-parser 这个 module。

首先在 server.js 的开头 require 这个 module:

接着在 server.listen() 之后用 router.use() 来执得解释,目标是解释成纯文本:

接着,修改一下 createItem() 函式,把项目的内容记录下来:

我们用 request.body 取得请求送过来的资料,将之存放在 item 里面,也顺便把 item 存到 todoList 里面,以备后面的读取使用。接着,我们会回应的时候,使用 Location 把网址转向 /todo/:id,也就是这个项目的网址。最后,将 item 内容回应出去。

重新运行 server.js。

为了测试这部分,curl 指令要传送资料,改为:

分解一下这个指令:

  • -X POST:指令用 POST 方式,默认是 GET;
  • –data ‘buy milk’:这就是我们要传送的资料;
  • –header ‘Content-Type: text/plain’:在 header 说明一下,资料格式是纯文本:text/plain。
  • http://localhost:3000/todo:这是我们要送达的目标地址。
  • -v:最后我加上了这个设定,要求 curl 显示完整资讯:

    可以看到,在第二段,也就是回应(response)的部分,有一项 Location: /todo/1,这就是我们在 response.writeHead() 里指定的。

执行这个指令之后,会得到 buy milk 的回应,也就是 response.end() 传回来的 item。同时 server.js 那边会得到:Create item  1 buy milk。意味着资料成功送到。

4. RESTful GET 读取一个待办事项

接下来,我们要通过 /item/:id 这个网址来读取单一个待办事项,在 server.js 最后加上以下程式:

可以看到,要取得请求网址里的 id ,可以用 request.params.id ,接着利用这个 id 从 todoList 里取出相对应的项目,再送到回应。要注意的是这里的用的是 router.get() 而不是 POST,而 Endpoint 的写法是: /todo/:id ,冒号后面的 id 就是 request.params.id 。

中间的 if 则是判断 item 存不存在。不存在的话,直接回应一个 404 ,把程式结束掉。

接着要测试这段程式,使用的 curl 指令更简单:

你在运行读取之前,记得先用之前的新增指令,建立新的待办事项,不然没东西可读。而我们目前的做法是没有将资料存进数据库,因此每次运行程式, todoList 都是空的。

5. RESTful DELETE 删除一个待办事项

删除跟读取是非常类似,同样把以下程式加到 server.js 里面:

我们直接把 todoList 里对应 id 的项目设为 undefined ,使之无效,response 则是什么也不用传回。而 router 那边则使用 .delete() 来处理。

同样地,中间用了 if 来判断 item 存不存在。

测试用的 curl 指令如下:

最后的 1 是 id ,记得先新增才有办法删除。

6. RESTful GET 读取整个待办事项列表

最后,我们要实现的是读取整个待办事项列表的功能。

这里我们用了 itemList 来储存有效的待办事项。用 For…in 遍历整个 todoList,并通过两个判断来找出所有有效的待办事项,找到后 push 到 itemList 里面:

  1. if ( !todoList.hasOwnProperty( id ) )  :判断这个 id 是否存在,不存在就马上换下一个 id;
  2. if ( typeof item !== 'string' )  :判断这个 id 的项目是否有效,也就是有没有被删除,如果无效,马上换一下个 id 。

接着就要把得到的列表送到 Console 以及 response。Console 方面,这里用了一个 JSON.stringify() 函式将资料以 JSON 字串的形式显示出来。Response 方面,我们用换行( ‘\n’ )将 Array 里面的字串组合成一字串,并存放在 listString 里面,最后把 listString 送出。

Router 方面用 .get() 来处理,Endpoint 是 /todo。

测试用的 Curl 指令如下:

没什么特别,直接用默认的 GET 方式打开 /list。执行这一句之前,记得先新增一些待办事项,不然就没东西可以列出。

7. 更新一个待办事项

更新跟新增很像,只是在 Endpoint 已经把 id 给你,所以你不需要把 counter 加 1 ,反而要检查这个 id 是不是存在于列表中,这就跟读取的检查一样。另外,跟新增一样,更新后,可以将 Endpoint 传回,也就是把 Location 设为 /todo/:id。而最后 router 使用的是 PUT 操作。

测试用的 curl 指令:

跟新增差不多,只是操作方法改为了 PUT,URI 最后加上 id。

Node.js RESTful API Todo List

8. 总结

在这个例子中,我们练习了:

  1. 三个常用的 Node.js 模组:http, router, bady-parser。
  2. Router 的 GET, POST, DELETE 操作。
  3. 用 curl 来对 RESTful API 进行测试。
  4. 使用 body-parser 解释 POST 传送过来的资料。

这个例子还可以继续加强地方有:

  1. 用户认证。也就是要提供用户帐号密码才可以进行任何操作。
  2. 使用数据库储存待办事项。
  3. 重构程式(Refactoring),例如,检查项目是否存在的判断重复出现,可以把它写成函式。

9. GitHub:下载程式码

你可以到 GitHub 上下载程式码。

GitHub 下载

若有任何问题或疑问的,欢迎在下面留言提出。

9. Change log

  • 03 Mar: 加入 Update 操作
  • 02 Mar: 更新 Endpoint 为: /todo

参考

3 Comments

  1. 您好:
    我按您的步骤做到3.1 Body Parser,前面都没问题,直到我贴上curl -X POST –data ‘buy milk’ –header ‘Content-Type: text/plain’ http://localhost:3000/todo -v后,出现curl: (6) Could not resolve host:..的问题,但是我反复测,都找不出问题来,请问您有遇过类似的问题或有什么建议吗?

  2. 需要把单引号换成双引号:curl -X POST –data “buy milk” –header “Content-Type: text/plain” http://localhost:3000/todo -v


Add a Comment

你的电子邮件位址并不会被公开。 必要字段标记为 *

Comment *
Name *
Email *
Website

这个网站采用 Akismet 服务减少垃圾留言。进一步了解 Akismet 如何处理网站访客的留言资料