REST APIs

在前面的章节中, 我们了解了Feathers 钩子 并创建了一个在NodeJS和浏览器中工作的消息服务.我们看到了Feathers如何自动发送事件, 但到目前为止我们并没有真正创建其他人可以使用的Web API.

这就是Feathers传输的目的.传输是一个插件, 可以将Feathers应用程序转换为服务器, 通过不同的协议公开我们的服务, 供其他客户端使用.由于传输涉及运行服务器, 因此它无法在浏览器中运行, 但我们稍后会了解到, 在浏览器Feathers应用程序中有连接到Feathers服务器的补充插件.

目前, Feathers正式拥有三种传输工具:

  • Express 用于通过JSON REST API公开服务

  • Socket.io 通过websockets连接服务并接收实时服务事件

  • Primus Socket.io的替代方案, 支持几个支持实时事件的websocket协议

在本章中, 我们将介绍HTTP REST传输和Feathers Express框架集成.

REST和服务

Feathers的目标之一是使构建 REST API 更容易, 因为它是迄今为止最常用的Web API协议. 例如, 我们想要发出像 GET/messages/1 这样的请求, 并获得一个JSON响应, 如 { "id": 1, "text": "The first message" }. 您可能已经注意到Feathers服务方法和HTTP方法如 GET, POST, PATCHDELETE 相互补充:

服务方式

HTTP方法

Path

.find()

GET

/messages

.get()

GET

/messages/1

.create()

POST

/messages

.update()

PUT

/messages/1

.patch()

PATCH

/messages/1

.remove()

DELETE

/messages/1

Feathers REST传输的基本功能是自动将现有服务方法映射到这些端点.

快速整合

Express 可能是用于创建Web应用程序和API的最流行的Node框架. Express 允许我们将Feathers应用程序转换为一个既是Feathers应用程序又是完全兼容的Express应用程序的应用程序.这意味着您可以使用诸如服务之类的Feathers功能以及任何现有的Express中间件.如前所述, Express框架集成仅适用于服务器.

要添加我们安装的集成 @feathersjs/express:

npm install @feathersjs/express --save

然后我们可以初始化一个Feathers和Express应用程序, 它将服务作为REST API在端口 3030 上公开:

const feathers = require('@feathersjs/feathers');
const express = require('@feathersjs/express');

// This creates an app that is both, an Express and Feathers app
const app = express(feathers());

// Turn on JSON body parsing for REST services
app.use(express.json());
// Turn on URL-encoded body parsing for REST services
app.use(express.urlencoded({ extended: true }));
// Set up REST transport using Express
app.configure(express.rest());

// Set up an error handler that gives us nicer errors
app.use(express.errorHandler());

// Start the server on port 3030
app.listen(3030);

express.json, express.urlencodedexpress.errorHandler 是正常的Express中间件.我们仍然可以使用 app.use 来注册Feathers服务.

小技巧

您可以在 Express 中找到有关Express框架集成的更多信息.

消息REST API

上面的代码实际上是我们将消息服务转换为REST API所需的全部内容.以下是我们的 app.js 的完整代码, 它通过REST API从 服务 公开服务:

const feathers = require('@feathersjs/feathers');
const express = require('@feathersjs/express');

class Messages {
  constructor() {
    this.messages = [];
    this.currentId = 0;
  }

  async find(params) {
    // Return the list of all messages
    return this.messages;
  }

  async get(id, params) {
    // Find the message by id
    const message = this.messages.find(message => message.id === parseInt(id, 10));

    // Throw an error if it wasn't found
    if(!message) {
      throw new Error(`Message with id ${id} not found`);
    }

    // Otherwise return the message
    return message;
  }

  async create(data, params) {
    // Create a new object with the original data and an id
    // taken from the incrementing `currentId` counter
    const message = Object.assign({
      id: ++this.currentId
    }, data);

    this.messages.push(message);

    return message;
  }

  async patch(id, data, params) {
    // Get the existing message. Will throw an error if not found
    const message = await this.get(id);

    // Merge the existing message with the new data
    // and return the result
    return Object.assign(message, data);
  }

  async remove(id, params) {
    // Get the message by id (will throw an error if not found)
    const message = await this.get(id);
    // Find the index of the message in our message array
    const index = this.messages.indexOf(message);

    // Remove the found message from our array
    this.messages.splice(index, 1);

    // Return the removed message
    return message;
  }
}

const app = express(feathers());

// Turn on JSON body parsing for REST services
app.use(express.json())
// Turn on URL-encoded body parsing for REST services
app.use(express.urlencoded({ extended: true }));
// Set up REST transport using Express
app.configure(express.rest());

// Initialize the messages service by creating
// a new instance of our class
app.use('messages', new Messages());

// Set up an error handler that gives us nicer errors
app.use(express.errorHandler());

// Start the server on port 3030
const server = app.listen(3030);

// Use the service to create a new message on the server
app.service('messages').create({
  text: 'Hello from the server'
});

server.on('listening', () => console.log('Feathers REST API started at http://localhost:3030'));

您可以通过运行来启动服务器

node app.js

注解

服务器将一直运行, 直到您通过终端中的 Control + C 停止它.记得每次 app.js 改变时停止并启动服务器.

重要

在Express中一个错误处理程序, 这里 app.use(express.errorHandler());, 总是必须是启动服务器之前的最后一行.

使用API

一旦服务器运行, 我们可以做的第一件事是在浏览器中点击 localhost:3030/messages. 由于我们已经在服务器上创建了一条消息, 因此JSON响应将如下所示:

[{"id":1,"text":"Hello from the server"}]

我们还可以通过转到 localhost:3030/messages/1 来检索该特定消息.

小技巧

一个浏览器插件, 如 适用于Chrome的JSON查看器, 可以更好地查看JSON响应.

现在可以通过在命令行上使用cURL将带有JSON数据的POST请求发送到同一URL来创建新消息, 如下所示:

curl -X POST \
  http://localhost:3030/messages/ \
  -H 'Content-Type: application/json' \
  -d '{ "text": "Hello from the command line!" }'

注解

您还可以使用 Postman 等工具发出HTTP请求.

如果您现在刷新 localhost:3030/messages, 您将看到新创建的消息.

我们还可以通过向其URL发送 DELETE 来删除消息:

curl -X DELETE \
  http://localhost:3030/messages/1

下一步是什么?

在本章中, 我们构建了一个功能完备的消息REST API.您可能已经可以想象我们的消息服务如何将其数据存储在数据库中而不是 messages 数组中.在 下一章 中, 让我们看看一些实现不同数据库的服务, 允许我们用更少的代码创建这些API!