本站资源收集于互联网,不提供软件存储服务,每天免费更新优质的软件以及学习资源!

使用NodeJS和Restify设计RESTfulAPI

网络教程 app 1℃

使用NodeJS和Restify设计RESTfulAPI

RESTful API 包含两个主要概念:资源和表示。资源可以是与数据关联的任何对象,或者用 URI 标识(多个 URI 可以引用同一资源),并且可以使用 HTTP 方法进行操作。表示是显示资源的方式。在本教程中,我们将介绍有关 RESTful API 设计的一些理论信息,并使用 NodeJS 实现示例博客应用程序 API。

资源

为 RESTful API 选择正确的资源是设计的一个重要部分。首先,您需要分析您的业务领域,然后决定使用与您的业务需求相关的资源数量和类型。如果您正在设计博客 API,您可能会使用文章、用户和评论。这些是资源名称,与之关联的数据是资源本身:

{ "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"}

资源动词

确定所需资源后,您可以继续进行资源操作。这里的操作指的是HTTP方法。例如,为了创建一篇文章,您可以提出以下请求:

POST /articles HTTP/1.1Host: localhost:3000Content-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"}

以同样的方式,您可以通过发出以下请求来查看现有文章:

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

更新现有文章怎么样?我听到你在说:

我可以使用有效负载向 /articles/update/123456789012 发出另一个 POST 请求。

也许更好,但是 URI 变得越来越复杂。正如我们前面所说,操作可以引用HTTP方法。这意味着,在 HTTP 方法中声明更新操作,而不是将其放入 URI 中。例如:

PUT /articles/123456789012 HTTP/1.1Host: localhost:3000Content-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"}

顺便说一下,在此示例中您会看到标签和类别字段。这些不需要是必填字段。您可以将它们留空并在将来设置它们。

有时,您需要在文章过时时将其删除。在这种情况下,您可以对 /articles/123456789012 使用DELETEHTTP 请求。

HTTP 方法是标准概念。如果您将它们用作操作,您将拥有简单的 URI,而这种简单的 API 将帮助您赢得满意的消费者。

如果您想在文章中插入评论怎么办?您可以选择文章并向所选文章添加新评论。通过使用此语句,您可以使用以下请求:

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

以上形式的资源称为子资源。 评论是文章的子资源。 上面的评论有效负载将作为文章的子项插入到数据库中。有时,不同的 URI 引用相同的资源。例如,要查看特定评论,您可以使用:

GET /articles/123456789012/ments/123 HTTP/1.1Host: localhost:3000Content-Type: application/json

或者:

GET /ments/123456789012 HTTP/1.1Host: localhost:3000Content-Type: application/json

版本控制

一般来说,API 功能会经常更改,以便为消费者提供新功能。在这种情况下,同一 API 的两个版本可以同时存在。为了分离这两个功能,您可以使用版本控制。版本控制有两种形式

    URI 中的版本:您可以在 URI 中提供版本号。例如,/v1.1/articles/123456789012。 标头中的版本:在标头中提供版本号,并且切勿更改 URI。 例如:

GET /articles/123456789012 HTTP/1.1Host: localhost:3000Accept-Version: 1.0

实际上,版本改变的只是资源的表示形式,而不是资源的概念。因此,您不需要更改 URI 结构。在 v1.1 中,可能在 Article 中添加了一个新字段。但是,它仍然返回一篇文章。在第二个选项中,URI 仍然很简单,消费者不需要在客户端实现中更改其 URI。

针对消费者不提供版本号的情况设计策略非常重要。当未提供版本时,您可以引发错误,也可以使用第一个版本返回响应。如果您使用最新的稳定版本作为默认版本,消费者的客户端实现可能会出现许多错误。

表示

表示是 API 显示资源的方式。当您调用 API 端点时,您将返回一个资源。该资源可以是任何格式,例如 XML、JSON 等。如果您正在设计新的 API,则最好使用 JSON。但是,如果您要更新用于返回 XML 响应的现有 API,则可以为 JSON 响应提供另一个版本。

有关 RESTful API 设计的理论信息已经足够了。让我们通过使用 Restify 设计和实现博客 API 来看看现实生活中的用法。

博客 REST API设计

为了设计 RESTful API,我们需要分析业务领域。然后我们就可以定义我们的资源。在博客 API 中,我们需要:

创建、更新、删除、查看文章为特定文章创建评论、更新、删除、查看、评论

创建、更新、删除、查看用户

在此 API 中,我不会介绍如何验证用户身份以创建文章或评论。对于身份验证部分,您可以参考 AngularJS & NodeJS 的基于令牌的身份验证教程。

我们的资源名称已准备就绪。资源操作就是简单的CRUD。您可以参考下表来了解 API 的总体展示。

资源名称 HTTP 动词 HTTP 方法

文章创建文章

更新文章

删除文章

查看文章

POST /articles with Payload

PUT /articles/123 with Payload

DELETE /articles/123

GET /article/123

评论创建评论

更新评论

删除评论

查看评论

带负载的 POST /articles/123/ments

带负载的 PUT /ments/123

删除 /ments/123

GET /ments/123

用户创建用户

更新用户

删除用户

查看用户

POST /users with Payload

PUT /users/123 with Payload

DELETE /users/123

GET /users/123

项目设置

在此项目中,我们将使用 NodeJS 和 Restify。资源将保存在 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);

评论

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);

用户

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

您可能会问这个猫鼬模块来自哪里。它是作为 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 Startserver.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 Startserver.post("/ments", controllers.ment.createComment)server.put("/ments/:id", controllers.ment.viewComment)server.del("/ments/:id", controllers.ment.deleteComment)server.get("/ments/:id", controllers.ment.viewComment)// Comment Endvar 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))) })

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

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

article.js

var mongoose = require(‘mongoose’), Article = mongoose.model("Article"), ObjectId = mongoose.Types.ObjectIdexports.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"}) } })}

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

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

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

ment.js

var mongoose = require(‘mongoose’), Comment = mongoose.model("Comment"), Article = mongoose.model("Article"), ObjectId = mongoose.Types.ObjectIdexports.viewComment = function(req, res) { Article.findOne({"ments._id": new ObjectId(req.params.id)}, {"ments.$": 1}, function(err, ment) { if (err) {res.status(500);res.json({ type: false, data: "Error occured: " + err}) } else {if (ment) { res.json({ type: true, data: new Comment(ment.ments[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( {"ments._id": new ObjectId(req.params.id)}, {"$set": {"ments.$.text": updatedCommentModel.text, "ments.$.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({"ments._id": new ObjectId(req.params.id)}, {"$pull": {"ments": {"_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" })} } })}

当您向某个资源 URI 发出请求时,控制器中声明的相关函数将被执行。控制器文件中的每个函数都可以使用 req 和 res 对象。这里的评论资源是文章的子资源。 所有的查询操作都是通过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,您将始终拥有一个灵活、可维护、易于理解的系统。

以上就是使用NodeJS和Restify设计RESTful API的详细内容,更多请关注范的资源库其它相关文章!

转载请注明:范的资源库 » 使用NodeJS和Restify设计RESTfulAPI

喜欢 (0)