RESTful API design with Node.js (part one)

本教程将引导你了解API设计的基础知识,教你如何设置REST API的骨干以及如何以合理的方式来构建你的代码。每一段代码将会有一个解释,正确理解它的最好方法就是自己跟着这种方式来编写代码。

为编写代码做一些准备工作

1.安装MongoDB
2.安装node.js

上面这些准备好过后,然后我们创建项目并初始化,和安装一些依赖库:

mkdir restful-api-demo && cd restful-api-demo
npm init -y
npm install express -S //Web开发框架
npm install mongoose -S //将应用程序的数据通过数据库的一组严格规则映射到数据库
npm install body-parser -S //是一个中间件,我们用它来解析通过HTTP请求发送的数据
npm install nodemon -D //监控node.js应用程序中的任何更改并自动重新启动服务器

你终于可以开始写一些代码来

首先,我们在根目录创建app.js文件.

//app.js
var express = require('express');
var app = express();

module.exports = app;

该文件将用于配置我们的应用程序,所有的逻辑将放在相应的目录中来实现特定的功能,使用module.exports使该app对象对程序的其余部分可见,我们可以使用require()来引入它。

现在我们需要告诉应用在哪里监听,在根目录创建一个文件server.js.

//server.js
var app = require('./app');
var port = process.env.PORT || 3000;

var server = app.listen(port, () => {
    console.log(`Express server listening on port ${port}`);
})

这里你引入app.js并把它放在一个变量中。然后选择应用程序的端口并通过app.listen来启动服务。
现在你需要配置一下package.json

//package.json
"script": {
    "start": "nodemon server.js"
}

然后运行npm start,在你的命令行中会出现Express server listening on port 3000

你已经运行起了服务器,非常好!

随着服务器的启动和运行,现在你需要连接数据库。在你的根目录创建一个文件db.js,它将用来存储你的数据库连接.

//db.js
var mongoose = require('mongoose');
mongoose.connect(‘mongodb://yourDatabaseURI’) //如果你没有改过mongodb的端口好,默认是'mongodb://localhost:27017'

你现在需要告诉应用程序它有一个可用的连接,在app.js中引入它.

var express = require('express');
var app = express();
var db = require('./db'); //添加这条

module.exports = app;

通过像这样指定,我们需要在app.js引入它并包含它.现在我们的应用程序知道它有一个数据库准备好并等待被访问。

你的程序现在应该有三个文件:

1.app.js配置应用程序
2.db.js用于指定与数据库的连接
3.server.js选择特定的端口,启动服务器节点

我们现在准备开始编写程序的应用逻辑

编写业务逻辑

首先创建一个名为user的文件夹,该文件夹将包含我们与用户数据库通信的所有文件。你将创建一个用户模型,此模型将作为显示数据库中所有用户的外观蓝图,在user文件夹下创建User.js

//User.js
var mongoose = require('mongoose');
var UserSchema = new mongoose.Schema({
    name: String,
    email: String,
    password: String
});
mongoose.model('User', UserSchema);

module.export = mongoose.model('User');

你正在创建一个模式,它将为数据库中每个用户提供特定的外观(根据我的理解,类似与关系型数据库中的表头),一个用户有一个name、email、password.通过指定mongoose.model('User', UserSchema)你将模式的布局绑定到名为'User'的模型.这就是你将用来访问数据库中的数据,导出出以便于你可以在程序的其它部分中引入。

现在进入有趣的部分,为你的应用程序创建路由并将其绑定到相应的操作中。

创建一个文件UserController.js,你会用它的名字来命名一个控制器,不仅是为来简单,还因为它将包含控制数据流入和流出数据库的操作.

//UserController.js
var express = require('express');
var router = express.Router();
var bodyParser = require('body-parser');
router.use(bodyParser.urlencoded({ extended: true }));
router.use(bodyParser.json());

var User = require('./user');

module.exports = router;

以上是用户控制器的代码。你将使用express的路由器来创建模块化并且独立于整个应用程序的子路由,如果你需要重新构建应用程序,你可以很容易做到这点,因为这种方法使你能够从一个地方插入并将其插入其它地方。body-parser模块被用来作为一个中间件以更优雅的方式处理数据,这在使用表单通过HTTP请求发送数据时会派上用场。

有趣的部分是require('./User'),现在你应该明白为什么在User.js导出这个模型,你将开始在UserController.js中使用它。当你像上面那样创建模型,它自动接受所有与数据库交互的必要方法,包括create、read、update和delete.最后,在文件的底部导出路由器,因为它将在app.js中需要。

我离题了

简单介绍一下REST,以更好的理解我们处理与数据库交互的4个行为,他们被叫做CRUD,分别指Create、Read、Update、Delete.使用HTTP请求,我们可以使用相应的操作触发这四个CRUD操作中的每一个。

  • POST用于将数据发送到服务器---Create
  • GET用于从服务器获取数据---Read
  • PUT用于发送和更新数据---Update
  • DELETE用于删除数据---Delete

回到业务逻辑那里

//UserController.js
var express = require("express");
var router = express.Router();
var bodyParser = require("body-parser");
router.use(bodyParser.urlencoded({ extended: true }));
router.use(bodyParser.json());

var User = require("./User");

//添加这部分
//创建一个新用户
router.post("/", (req, res) => {
  User.create(
    {
      name: req.body.name,
      email: req.body.email,
      password: req.body.password
    },
    (err, user) => {
      if (err)
        return res
          .status(500)
          .send("There was a problem adding the information to the database.");
      res.status(200).send(user);
    }
  );
});

//返回数据库中的所有数据
router.get("/", (req, res) => {
  User.find({}, (err, users) => {
    if (err)
      return res.status(500).send("There was a problem finding the users.");
    res.status(200).send(users);
  });
});

module.exports = router;

看看第一种方法,你会看到路由器对象有一个.post方法并有两个参数,第一个参数是将被链接到一个函数的路由,这个函数是第二个参数,它需要另外两个参数来表示对服务器的请求和来自服务器的响应,在函数内部,你将使用上面所需的用户模型,用户模型有一个create方法,它也需要两个参数,第一个参数是一个对象,第二个参数是一个函数,该对象包含到要插入到数据库的值,你仔细观察,他们的结构就像你上面创建的模式,在创建之后,回调函数中被另外两个参数调用,一个错误值和一个成功值,你将在创建新用户期间检查是否有错误,并做出相应的回应,或者如果一切正常,则用新创建的用户的数据作出回应。

第二个方法更简单一些,路由器对象有一个.post方法,它也需要两个参数,在函数内,你在用户模型上调用find()方法,它也接受两个参数,Find是一种从数据库返回值的方法,第一个参数是一个对象,定义了返回值必须满足的要求。正如在这个例子中,对象是空的,数据库中所有用户都将被返回。

现在,你需要让应用程序知道它有一个用户控制器,在app.js中加入

//app.js
var express = require('express');
var app = express();
var db = require('./db');

//添加这两行
var UserController = require('./user/UserController');
app.use('/users', UserController);

module.exports = app;

你引入用户控制器和使用app.use来告诉应用程序链接到路由/users.现在,你的用户控制器中的/将映射到/users.

现在我们来测试一下,我们使用Postman这个工具,你输入你想要发送HTTP请求的URL,选择一种方法,如果需要,传入参数,我们发送POST请求到http://localhost:3000/users,这意味这你需要在请求主题中输入一组参数。

create

在Postman选择body输入name、email、password,该响应将包含创建的用户匹配数据。

现在,继续并将HTTP方法更改为GET,然后再次点击发送。你会看到所有创建的用户都从数据库中返回。继续前进,稍微玩一下,添加更多的用户,并观察GET请求的行为。

回到代码

到目前为止,你已经建立了一个服务器,配置你的应用程序,将其连接到数据库并创建了两个用于与数据库通信的API。这里还有三个API需要执行:从数据库中检索单个用户、更新一个用户信息、删除一个用户,所有这些都有一个共同点,他们都会有一个查询参数,通常称为路由参数。不要害怕,这只是一个值,将会通过请求传递。

//UserController.js

//...

//从数据库中获取单个用户
router.get("/:id", (req, res) => {
  User.findById(req.params.id, (err, user) => {
    if (err)
      return res.status(500).send("There was a problem finding the user.");
    if (!user) return res.status(404).send("No user found.");
    res.status(200).send(user);
  });

你现在已经添加一个GET请求,但是仔细看看路由,router.get方法的第一个参数,它现在由一个冒号和一些文本组成的值,这个表示法意味着它是一个查询参数的占位符,一个简单的值,这将随请求一起发送,传递给'/:id'的值将通过req.params对象访问,其中查询参数的名称将映射到req.params对象上具有相同名称的属性。

Mongoose有一个叫做.findById的方法,它只返回需要查询的ID的信息,Id是第一个参数,回调函数是第二个参数,现在你应该能发现一些共同点:所有的Mongoose方法都需要一个值作为第一个参数和一个回调函数作为最后一个参数,此回调将在数据库返回查询值后调用,DELETE请求也可以看到相同的模式。

//UserCOntroller.js

//...

//从数据库中删除一个用户
router.delete("/:id", (req, res) => {
  User.findByIdAndRemove(req.params.id, (err, user) => {
    if (err)
      return res.status(500).send("There was a problem deleting the user.");
    res.status(200).send(`User ${user.name} was deleted.`);
  });
});

router.delete的方法看起来与上面的router.get完全相同。除了它的名字外,Mongoose的方法看起来都一样。你将通过给该方法指定要删除的用户的ID来从数据库中删除用户。 .findByIdAndRemove方法将像findById一样查找用户,但是它将从数据库中删除一个用户。

应用程序的最后一步是实现从数据库中更新已经存在的用户,这是通过PUT请求完成的。

//UserController.js

//...

//更新数据库中的单个用户
router.put("/:id", (req, res) => {
  User.findByIdAndUpdate(
    req.params.id,
    req.body,
    { new: true },
    (err, user) => {
      if (err)
        return res.status(500).send("There was a problem updating the user.");
      res.status(200).send(user);
    }
  );
});

router.put请求与上面写的两个请求非常相似。它也是需要一个查询参数,ID。与众不同的是,它同时也需要body参数,就像你先写的POST请求一样。请记住,具有主体唯一的HTTP方法是POST和PUT。

我们来分析一下.findByIdAndUpdate做了什么?这种方法与上面写的有些不同,它有三个主要参数:一个ID、与其值将被更新的用户对应的对象、当然还有一个回调函数。你把第二个参数作为请求主体,这个对象,因为它已经被我们所需要的并且在UserController.js顶部使用的中间件正确解析了。更新某些值时的一个很好的做法是请求将更新的值发回给你。这一点很重要,因为你想要返回更新的值。第三个参数代表哪个版本值的选项。

准备测试

GET

PUT

DELETE

完美!刚开始写或许会比较困难,但当多看几遍过后,你会发现很多共同点。我已经把代码上传到github,如果你需要的话,请点击这里.

参考链接
    RESTful API design with Node.js by Adnan Rahić

相关文章

此处评论已关闭