RESTful API には、resource と representation という 2 つの主要な概念が含まれています。リソースは、データに関連付けられたオブジェクト、または URI によって識別されるオブジェクト (複数の URI が同じリソースを参照できます) であり、HTTP メソッドを使用して操作できます。表現とは、リソースを表示する方法です。このチュートリアルでは、RESTful API 設計に関する理論的な情報をいくつか取り上げ、NodeJS を使用してサンプル ブログ アプリケーション API を実装します。
###リソース###、Users、および Comments を使用できます。これらはリソース名であり、それらに関連付けられたデータはリソース自体です: リーリー リソース動詞
同様に、次のリクエストを行うことで既存の記事を表示できます:
リーリー既存の記事を更新してみてはいかがでしょうか?あなたの声が聞こえます:
ペイロードを使用して、/articles/update/123456789012 に対して別の POST リクエストを行うことができます。
もっと良いかもしれませんが、URI はより複雑になっています。前に述べたように、操作は HTTP メソッドを参照できます。これは、update
オペレーションを URI に入れるのではなく、HTTP メソッドで宣言することを意味します。例えば:### リーリー ちなみに、この例では、タグ フィールドとカテゴリ フィールドが表示されます。これらは必須フィールドである必要はありません。空白のままにして、将来的に設定することもできます。
記事が古くなった場合、記事を削除する必要がある場合があります。この場合、DELETE
HTTP リクエストを/articles/123456789012 に使用できます。
HTTP メソッドは標準的な概念です。これらをアクションとして使用すると、単純な URI が得られ、この単純な API は満足な消費者を獲得するのに役立ちます。記事にコメントを挿入したい場合はどうすればよいですか?記事を選択し、選択した記事に新しいコメントを追加できます。このステートメントを使用すると、次のリクエストを使用できます:
リーリー
上記の形式のリソースは、サブリソースと呼ばれます。
コメントは、記事のサブリソースです。 上記の Comments ペイロードは、Articles の子としてデータベースに挿入されます。場合によっては、異なる URI が同じリソースを参照することがあります。たとえば、特定のコメントを表示するには、次のように使用します: リーリー ###または:### リーリー ###バージョン管理### 一般的に、API 機能は消費者に新しい機能を提供するために頻繁に変更されます。この場合、同じ API の 2 つのバージョンが同時に存在する可能性があります。これら 2 つの機能を分離するには、バージョン管理を使用できます。バージョン管理には 2 つの形式があります
URI のバージョン:
/v1.1/articles/123456789012
ヘッダーのバージョン:
ヘッダーにバージョン番号を指定します。URI は決して変更しないでください。 ### ###例えば:###
リーリー
実際には、バージョンによって変更されるのはリソースの表現のみであり、リソースの概念は変更されません。したがって、URI 構造を変更する必要はありません。 v1.1 では、記事に新しいフィールドが追加されている可能性があります。ただし、それでも記事が返されます。 2 番目のオプションでは、URI は単純なままであり、コンシューマはクライアント実装で URI を変更する必要がありません。
RESTful API を設計するには、ビジネス ドメインを分析する必要があります。次に、リソースを定義します。 Blog API では次のものが必要です:
作成、更新、削除、表示
特定の
この API では、記事やコメントを作成するためにユーザーを認証する方法については説明しません。認証部分については、「AngularJS および NodeJS のトークンベースの認証チュートリアル」を参照してください。
リソース名が準備できました。リソース操作は単純な CRUD です。 API の概要については、以下の表を参照してください。
リソース名 | HTTP動詞 | HTTPメソッド |
---|---|---|
記事の作成 | 記事の更新 記事の削除 記事を表示 POST /articles (ペイロードあり) |
PUT /articles/123 (ペイロードあり) DELETE /articles/123 GET /article/123
|
###コメント###
コメントの更新 | コメントの削除 コメントの表示
ペイロード /articles/123/comments を使用した POST ペイロード /comments/123/123 を使用した PUT |
削除 /comments/123 GET /comments/123 ###ユーザー### ユーザーの作成 |
ユーザーの削除 | ユーザーの表示
POST /users (ペイロードあり) PUT /users/123 (ペイロードあり) 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 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))) }) ログイン後にコピー 在此代码片段中,首先迭代包含控制器方法的所有控制器文件,并初始化所有控制器,以便执行对 URI 的特定请求。之后,为基本的CRUD操作定义了具体操作的URI。 Article 上的其中一项操作也有版本控制。 例如,如果您在 Accept-Version 标头中将版本声明为 article.jsvar 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" }) } }) } ログイン後にコピー 您可以在下面找到 Mongoose 端基本 CRUD 操作的说明:
上面提到的 Mongoose 命令只是通过 Article 对象进行静态方法,该对象也是 Mongoose 模式的引用。 comment.jsvar 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" }) } } }) } ログイン後にコピー 当您向某个资源 URI 发出请求时,控制器中声明的相关函数将被执行。控制器文件中的每个函数都可以使用 req 和 res 对象。这里的评论资源是文章的子资源。 所有的查询操作都是通过Article模型进行的,以便找到子文档并进行必要的更新。但是,每当您尝试查看 Comment 资源时,即使 MongoDB 中没有集合,您也会看到一个 Comment 资源。 其他设计建议
最后,如果您按照这些基本规则设计 RESTful API,您将始终拥有一个灵活、可维护、易于理解的系统。 |
以上がNodeJS と Restify を使用した RESTful API の設計の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。