Heim > Web-Frontend > js-Tutorial > Hauptteil

Entwerfen einer RESTful-API mit NodeJS und Restify

WBOY
Freigeben: 2023-09-03 10:37:01
Original
1590 Leute haben es durchsucht

使用NodeJS和Restify设计RESTful API

RESTful API besteht aus zwei Hauptkonzepten: Ressourcen und Repräsentationen. Eine Ressource kann ein beliebiges Objekt sein, das mit Daten verknüpft ist oder durch einen URI identifiziert wird (mehrere URIs können auf dieselbe Ressource verweisen) und kann mithilfe von HTTP-Methoden manipuliert werden. Darstellung ist die Art und Weise, wie eine Ressource angezeigt wird. In diesem Tutorial behandeln wir einige theoretische Informationen zum RESTful-API-Design und implementieren eine Beispiel-API für eine Bloganwendung mit NodeJS.

Ressourcen

Die Auswahl der richtigen Ressourcen für eine RESTful-API ist ein wichtiger Teil des Designs. Zunächst müssen Sie Ihr Geschäftsfeld analysieren und dann über die Menge und Art der zu verwendenden Ressourcen entscheiden, die für Ihre Geschäftsanforderungen relevant sind. Wenn Sie eine Blogging-API entwerfen würden, könnten Sie Posts, Users und Comments verwenden. Dies sind die Ressourcennamen und die damit verbundenen Daten sind die Ressource selbst:

{
    "title": "How to Design RESTful API",
    "content": "RESTful API design is a very important case in the software development world.",
    "author": "huseyinbabal",
    "tags": [
        "technology",
        "nodejs",
        "node-restify"
        ]
    "category": "NodeJS"
}
Nach dem Login kopieren

Ressourcenverb

Nachdem Sie die benötigten Ressourcen identifiziert haben, können Sie mit den Ressourcenoperationen fortfahren. Die Operation bezieht sich hier auf die HTTP-Methode. Um beispielsweise einen Artikel zu erstellen, können Sie die folgende Anfrage stellen:

POST /articles HTTP/1.1
Host: localhost:3000
Content-Type: application/json

{
  "title": "RESTful API Design with Restify",
  "slug": "restful-api-design-with-restify",
  "content": "Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas.",
  "author": "huseyinbabal"
}
Nach dem Login kopieren

Auf die gleiche Weise können Sie vorhandene Artikel anzeigen, indem Sie die folgende Anfrage stellen:

GET /articles/123456789012 HTTP/1.1
Host: localhost:3000
Content-Type: application/json
Nach dem Login kopieren

Wie wäre es mit der Aktualisierung eines vorhandenen Artikels? Ich höre dich sagen:

Ich kann mit der Nutzlast eine weitere POST-Anfrage an /articles/update/123456789012 stellen.

Vielleicht besser, aber URIs werden immer komplexer. Wie bereits erwähnt, können Operationen auf HTTP-Methoden verweisen. Das bedeutet, dass der Update-Vorgang in der HTTP-Methode deklariert wird, anstatt ihn in den URI einzufügen. Zum Beispiel:

PUT /articles/123456789012 HTTP/1.1
Host: localhost:3000
Content-Type: application/json
{
    "title": "Updated How to Design RESTful API",
    "content": "Updated RESTful API design is a very important case in the software development world.",
    "author": "huseyinbabal",
    "tags": [
        "technology",
        "nodejs",
        "restify",
        "one more tag"
        ]
    "category": "NodeJS"
}
Nach dem Login kopieren

Übrigens sehen Sie in diesem Beispiel Tag- und Kategoriefelder. Dies müssen keine Pflichtfelder sein. Sie können sie leer lassen und sie später festlegen.

Manchmal müssen Sie einen Artikel löschen, wenn er veraltet ist. In diesem Fall können Sie eine DELETEHTTP-Anfrage an /articles/123456789012 verwenden.

HTTP-Methoden sind Standardkonzepte. Wenn Sie sie als Aktionen verwenden, erhalten Sie einfache URIs, und diese einfache API wird Ihnen dabei helfen, zufriedene Verbraucher zu gewinnen.

Was ist, wenn Sie einen Kommentar in Ihren Artikel einfügen möchten? Sie können Artikel auswählen und neue Kommentare zu den ausgewählten Artikeln hinzufügen. Mit dieser Anweisung können Sie die folgenden Anfragen verwenden:

POST /articles/123456789012/comments HTTP/1.1
Host: localhost:3000
Content-Type: application/json
{
    "text": "Wow! this is a good tutorial",
    "author": "john doe"
}
Nach dem Login kopieren

Ressourcen in der obigen Form werden Unterressourcen genannt. Kommentare sind Unterressourcen von Artikeln. Die obige Nutzlast „Kommentare“ wird als untergeordnetes Element der „Artikel“ in die Datenbank eingefügt. Manchmal verweisen verschiedene URIs auf dieselbe Ressource. Um beispielsweise einen bestimmten Kommentar anzuzeigen, können Sie Folgendes verwenden:

GET /articles/123456789012/comments/123 HTTP/1.1
Host: localhost:3000
Content-Type: application/json
Nach dem Login kopieren
oder:
GET /comments/123456789012 HTTP/1.1
Host: localhost:3000
Content-Type: application/json
Nach dem Login kopieren
Versionskontrolle

Im Allgemeinen ändert sich die API-Funktionalität häufig, um Verbrauchern neue Funktionen bereitzustellen. In diesem Fall können zwei Versionen derselben API gleichzeitig existieren. Um diese beiden Funktionen zu trennen, können Sie die Versionskontrolle verwenden. Versionskontrolle gibt es in zwei Formen

Version im URI:

Sie können die Versionsnummer im URI angeben. Zum Beispiel
  1. . /v1.1/articles/123456789012 Version im Header: Geben Sie die Versionsnummer im Header an und ändern Sie niemals den URI.
  2. Zum Beispiel:
    GET /articles/123456789012 HTTP/1.1
    Host: localhost:3000
    Accept-Version: 1.0
    
    Nach dem Login kopieren
    Tatsächlich ändert die Version nur die Darstellung der Ressource, nicht das Konzept der Ressource. Daher müssen Sie die URI-Struktur nicht ändern. In Version 1.1 wurde möglicherweise ein neues Feld im Artikel hinzugefügt. Es wird jedoch immer noch ein Artikel zurückgegeben. Bei der zweiten Option bleibt der URI einfach und der Verbraucher muss seinen URI in der Client-Implementierung nicht ändern.
  3. Es ist sehr wichtig, Strategien für Situationen zu entwickeln, in denen Verbraucher keine Versionsnummern angeben. Sie können einen Fehler auslösen, wenn keine Version bereitgestellt wird, oder eine Antwort mit der ersten Version zurückgeben. Wenn Sie die neueste stabile Version als Standardversion verwenden, kann die Client-Implementierung des Verbrauchers viele Fehler aufweisen.

bedeutet

stellt die Art und Weise dar, wie die API Ressourcen anzeigt. Wenn Sie einen API-Endpunkt aufrufen, geben Sie eine Ressource zurück. Die Ressource kann in einem beliebigen Format wie XML, JSON usw. vorliegen. Wenn Sie eine neue API entwerfen, verwenden Sie am besten JSON. Wenn Sie jedoch eine vorhandene API aktualisieren, die XML-Antworten zurückgibt, können Sie eine andere Version für JSON-Antworten bereitstellen.

Genügend theoretische Informationen zum RESTful-API-Design. Sehen wir uns die reale Nutzung an, indem wir mit Restify eine Blogging-API entwerfen und implementieren.

BLOG REST API

Design

Um eine RESTful-API zu entwerfen, müssen wir die Geschäftsdomäne analysieren. Dann können wir unsere Ressourcen definieren. In der Blog-API benötigen wir:

Erstellen, Aktualisieren, Löschen, Anzeigen von

Artikeln

  • Kommentare erstellen, aktualisieren, löschen, anzeigen,
  • Kommentare
  • für bestimmte Artikel

    Benutzer erstellen, aktualisieren, löschen und anzeigen

In dieser API werde ich nicht darauf eingehen, wie ein Benutzer authentifiziert wird, um einen Artikel oder Kommentar zu erstellen. Informationen zum Authentifizierungsteil finden Sie im Tutorial zur tokenbasierten Authentifizierung für AngularJS und NodeJS.

Unsere Ressourcennamen sind fertig. Ressourcenoperationen sind einfaches CRUD. Eine Übersicht über die API finden Sie in der folgenden Tabelle.

Ressourcenname HTTP-Verb HTTP-Methode
Artikel Artikel erstellen

Artikel aktualisieren

Artikel löschen

Artikel anzeigen

POST /articles mit Payload

PUT /articles/123 mit Payload

DELETE /articles/123

GET /article/123

Kommentare Kommentar erstellen

Kommentar aktualisieren

Kommentar löschen

Kommentar anzeigen

POST /articles/123/comments mit Nutzlast

PUT /comments/123 mit Nutzlast

DELETE /comments/123

GET /comments/123

Benutzer Benutzer erstellen

Benutzer aktualisieren

Benutzer löschen

Benutzer anzeigen

POST /users mit Payload

PUT /users/123 mit Payload

DELETE /users/123

GET /users/123

项目设置

在此项目中,我们将使用 NodeJSRestify。资源将保存在 MongoDB 数据库中。首先,我们可以在Restify中将资源定义为模型。

文章

var mongoose = require("mongoose");
var Schema   = mongoose.Schema;

var ArticleSchema = new Schema({
    title: String,
    slug: String,
    content: String,
    author: {
        type: String,
        ref: "User"
    }
});
mongoose.model('Article', ArticleSchema);
Nach dem Login kopieren

评论

var mongoose = require("mongoose");
var Schema   = mongoose.Schema;

var CommentSchema = new Schema({
    text: String,
    article: {
        type: String,
        ref: "Article"
    },
    author: {
        type: String,
        ref: "User"
    }
});
mongoose.model('Comment', CommentSchema);
Nach dem Login kopieren

用户

不会对用户资源进行任何操作。我们假设我们已经知道能够对文章或评论进行操作的当前用户。

您可能会问这个猫鼬模块来自哪里。它是作为 NodeJS 模块编写的最流行的 MongoDB ORM 框架。该模块包含在项目的另一个配置文件中。

现在我们可以为上述资源定义 HTTP 动词。您可以看到以下内容:

var restify = require('restify')
    , fs = require('fs')


var controllers = {}
    , controllers_path = process.cwd() + '/app/controllers'
fs.readdirSync(controllers_path).forEach(function (file) {
    if (file.indexOf('.js') != -1) {
        controllers[file.split('.')[0]] = require(controllers_path + '/' + file)
    }
})

var server = restify.createServer();

server
    .use(restify.fullResponse())
    .use(restify.bodyParser())

// Article Start
server.post("/articles", controllers.article.createArticle)
server.put("/articles/:id", controllers.article.updateArticle)
server.del("/articles/:id", controllers.article.deleteArticle)
server.get({path: "/articles/:id", version: "1.0.0"}, controllers.article.viewArticle)
server.get({path: "/articles/:id", version: "2.0.0"}, controllers.article.viewArticle_v2)
// Article End

// Comment Start
server.post("/comments", controllers.comment.createComment)
server.put("/comments/:id", controllers.comment.viewComment)
server.del("/comments/:id", controllers.comment.deleteComment)
server.get("/comments/:id", controllers.comment.viewComment)
// Comment End

var port = process.env.PORT || 3000;
server.listen(port, function (err) {
    if (err)
        console.error(err)
    else
        console.log('App is ready at : ' + port)
})

if (process.env.environment == 'production')
    process.on('uncaughtException', function (err) {
        console.error(JSON.parse(JSON.stringify(err, ['stack', 'message', 'inner'], 2)))
    })
Nach dem Login kopieren

在此代码片段中,首先迭代包含控制器方法的所有控制器文件,并初始化所有控制器,以便执行对 URI 的特定请求。之后,为基本的CRUD操作定义了具体操作的URI。 Article 上的其中一项操作也有版本控制。

例如,如果您在 Accept-Version 标头中将版本声明为 2,则将执行 viewArticle_v2viewArticleviewArticle_v2 都执行相同的工作,显示资源,但它们以不同的格式显示文章资源,正如您在 中看到的那样title 字段如下。最后,服务器在特定端口上启动,并应用一些错误报告检查。我们可以继续使用控制器方法对资源进行 HTTP 操作。

article.js

var mongoose = require('mongoose'),
    Article = mongoose.model("Article"),
    ObjectId = mongoose.Types.ObjectId

exports.createArticle = function(req, res, next) {
    var articleModel = new Article(req.body);
    articleModel.save(function(err, article) {
        if (err) {
            res.status(500);
            res.json({
                type: false,
                data: "Error occured: " + err
            })
        } else {
            res.json({
                type: true,
                data: article
            })
        }
    })
}

exports.viewArticle = function(req, res, next) {
    Article.findById(new ObjectId(req.params.id), function(err, article) {
        if (err) {
            res.status(500);
            res.json({
                type: false,
                data: "Error occured: " + err
            })
        } else {
            if (article) {
                res.json({
                    type: true,
                    data: article
                })
            } else {
                res.json({
                    type: false,
                    data: "Article: " + req.params.id + " not found"
                })
            }
        }
    })
}

exports.viewArticle_v2 = function(req, res, next) {
    Article.findById(new ObjectId(req.params.id), function(err, article) {
        if (err) {
            res.status(500);
            res.json({
                type: false,
                data: "Error occured: " + err
            })
        } else {
            if (article) {
                article.title = article.title + " v2"
                res.json({
                    type: true,
                    data: article
                })
            } else {
                res.json({
                    type: false,
                    data: "Article: " + req.params.id + " not found"
                })
            }
        }
    })
}

exports.updateArticle = function(req, res, next) {
    var updatedArticleModel = new Article(req.body);
    Article.findByIdAndUpdate(new ObjectId(req.params.id), updatedArticleModel, function(err, article) {
        if (err) {
            res.status(500);
            res.json({
                type: false,
                data: "Error occured: " + err
            })
        } else {
            if (article) {
                res.json({
                    type: true,
                    data: article
                })
            } else {
                res.json({
                    type: false,
                    data: "Article: " + req.params.id + " not found"
                })
            }
        }
    })
}

exports.deleteArticle = function(req, res, next) {
    Article.findByIdAndRemove(new Object(req.params.id), function(err, article) {
        if (err) {
            res.status(500);
            res.json({
                type: false,
                data: "Error occured: " + err
            })
        } else {
            res.json({
                type: true,
                data: "Article: " + req.params.id + " deleted successfully"
            })
        }
    })
}
Nach dem Login kopieren

您可以在下面找到 Mongoose 端基本 CRUD 操作的说明:

  • createArticle:这是对从请求正文发送的 articleModel 的简单保存操作。可以通过将请求正文作为构造函数传递给模型来创建新模型,例如 vararticleModel = new Article(req.body)
  • viewArticle:为了查看文章详细信息,URL 参数中需要提供文章 ID。 findOne 带有 ID 参数足以返回文章详细信息。
  • updateArticle:文章更新是一个简单的查找查询并对返回的文章进行一些数据操作。最后,需要通过发出 save 命令将更新后的模型保存到数据库中。
  • deleteArticle:findByIdAndRemove 是通过提供文章 ID 来删除文章的最佳方法。

上面提到的 Mongoose 命令只是通过 Article 对象进行静态方法,该对象也是 Mongoose 模式的引用。

comment.js

var mongoose = require('mongoose'),
    Comment = mongoose.model("Comment"),
    Article = mongoose.model("Article"),
    ObjectId = mongoose.Types.ObjectId

exports.viewComment = function(req, res) {
    Article.findOne({"comments._id": new ObjectId(req.params.id)}, {"comments.$": 1}, function(err, comment) {
        if (err) {
            res.status(500);
            res.json({
                type: false,
                data: "Error occured: " + err
            })
        } else {
            if (comment) {
                res.json({
                    type: true,
                    data: new Comment(comment.comments[0])
                })
            } else {
                res.json({
                    type: false,
                    data: "Comment: " + req.params.id + " not found"
                })
            }
        }
    })
}

exports.updateComment = function(req, res, next) {
    var updatedCommentModel = new Comment(req.body);
    console.log(updatedCommentModel)
    Article.update(
        {"comments._id": new ObjectId(req.params.id)},
        {"$set": {"comments.$.text": updatedCommentModel.text, "comments.$.author": updatedCommentModel.author}},
        function(err) {
            if (err) {
                res.status(500);
                res.json({
                    type: false,
                    data: "Error occured: " + err
                })
            } else {
                res.json({
                    type: true,
                    data: "Comment: " + req.params.id + " updated"
                })
            }
    })
}

exports.deleteComment = function(req, res, next) {
    Article.findOneAndUpdate({"comments._id": new ObjectId(req.params.id)},
        {"$pull": {"comments": {"_id": new ObjectId(req.params.id)}}},
        function(err, article) {
        if (err) {
            res.status(500);
            res.json({
                type: false,
                data: "Error occured: " + err
            })
        } else {
            if (article) {
                res.json({
                    type: true,
                    data: article
                })
            } else {
                res.json({
                    type: false,
                    data: "Comment: " + req.params.id + " not found"
                })
            }
        }
    })
}
Nach dem Login kopieren

当您向某个资源 URI 发出请求时,控制器中声明的相关函数将被执行。控制器文件中的每个函数都可以使用 reqres 对象。这里的评论资源是文章的子资源。 所有的查询操作都是通过Article模型进行的,以便找到子文档并进行必要的更新。但是,每当您尝试查看 Comment 资源时,即使 MongoDB 中没有集合,您也会看到一个 Comment 资源。

其他设计建议

  • 选择易于理解的资源,以便消费者轻松使用。
  • 让业务逻辑由消费者实现。例如,文章资源有一个名为 slug 的字段。 消费者不需要将此详细信息发送到 REST API。这种 slug 策略应该在 REST API 端进行管理,以减少 API 和消费者之间的耦合。消费者只需要发送标题详细信息,您就可以在REST API端根据您的业务需求生成slug。
  • 为您的 API 端点实施授权层。未经授权的消费者可以访问属于其他用户的受限数据。在本教程中,我们没有介绍用户资源,但您可以参阅使用 AngularJS 和 NodeJS 进行基于令牌的身份验证,以获取有关 API 身份验证的更多信息。
  • 用户 URI 而不是查询字符串。 /articles/123  (好),/articles?id=123(差)。
  • 不保留状态;始终使用即时输入/输出。
  • 使用名词来表示您的资源。您可以使用 HTTP 方法来操作资源。

最后,如果您按照这些基本规则设计 RESTful API,您将始终拥有一个灵活、可维护、易于理解的系统。

Das obige ist der detaillierte Inhalt vonEntwerfen einer RESTful-API mit NodeJS und Restify. Für weitere Informationen folgen Sie bitte anderen verwandten Artikeln auf der PHP chinesischen Website!

Quelle:php.cn
Erklärung dieser Website
Der Inhalt dieses Artikels wird freiwillig von Internetnutzern beigesteuert und das Urheberrecht liegt beim ursprünglichen Autor. Diese Website übernimmt keine entsprechende rechtliche Verantwortung. Wenn Sie Inhalte finden, bei denen der Verdacht eines Plagiats oder einer Rechtsverletzung besteht, wenden Sie sich bitte an admin@php.cn
Beliebte Tutorials
Mehr>
Neueste Downloads
Mehr>
Web-Effekte
Quellcode der Website
Website-Materialien
Frontend-Vorlage